diff --git a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml index 63b09e6dbb6a0..0aa750cafa08b 100755 --- a/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml +++ b/eng/code-quality-reports/src/main/resources/checkstyle/checkstyle-suppressions.xml @@ -252,6 +252,7 @@ + @@ -550,7 +551,7 @@ - + diff --git a/sdk/core/azure-core-experimental/CHANGELOG.md b/sdk/core/azure-core-experimental/CHANGELOG.md index cccf1ca88f90c..a5b10867e9c32 100644 --- a/sdk/core/azure-core-experimental/CHANGELOG.md +++ b/sdk/core/azure-core-experimental/CHANGELOG.md @@ -2,6 +2,8 @@ ## 1.0.0-beta.6 (Unreleased) +- Added : `BinaryData` abstraction to represent binary data and supports serialization through `ObjectSerializer`. + ## 1.0.0-beta.5 (2020-10-01) - Added `JsonPatchDocument` to support JSON Patch functionality. diff --git a/sdk/core/azure-core-experimental/pom.xml b/sdk/core/azure-core-experimental/pom.xml index 86da5c028103c..cab7a7e194944 100644 --- a/sdk/core/azure-core-experimental/pom.xml +++ b/sdk/core/azure-core-experimental/pom.xml @@ -79,6 +79,12 @@ 5.6.2 test + + io.projectreactor + reactor-test + 3.3.9.RELEASE + test + diff --git a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/util/BinaryData.java b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/util/BinaryData.java new file mode 100644 index 0000000000000..2979599d21ca1 --- /dev/null +++ b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/util/BinaryData.java @@ -0,0 +1,285 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.core.experimental.util; + +import com.azure.core.util.FluxUtil; +import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.serializer.ObjectSerializer; +import com.azure.core.util.serializer.TypeReference; +import static com.azure.core.util.FluxUtil.monoError; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Objects; + +/** + * This class is an abstraction over many different ways that binary data can be represented. The {@link BinaryData} + * can be created from {@link InputStream}, {@link Flux} of {@link ByteBuffer}, {@link String}, {@link Object} and byte + * array. The data is collected from provided sources and stored into a byte array. + *

+ * It also provides a way to serialize and deserialize an {@link Object} into {@link BinaryData} given an + * {@link ObjectSerializer}. Code samples are explained below. + * + *

Create an instance from Bytes

+ * {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#bytes} + * + *

Create an instance from String

+ * {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#String} + * + *

Create an instance from InputStream

+ * {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#Stream} + * + *

Get an Object from {@link BinaryData}

+ * {@codesnippet com.azure.core.experimental.util.BinaryDocument.to#ObjectAsync} + * + * @see ObjectSerializer + */ +public final class BinaryData { + private static final ClientLogger LOGGER = new ClientLogger(BinaryData.class); + private final byte[] data; + + /** + * Create instance of {@link BinaryData} given the data. + * @param data to represent as bytes. + * @throws NullPointerException If {@code data} is null. + */ + BinaryData(byte[] data) { + Objects.requireNonNull(data, "'data' cannot be null."); + this.data = Arrays.copyOf(data, data.length); + } + + /** + * Provides {@link InputStream} for the data represented by this {@link BinaryData} object. + * + *

Get InputStream from BinaryData

+ * {@codesnippet com.azure.core.experimental.util.BinaryDocument.to#Stream} + * + * @return {@link InputStream} representing the binary data. + */ + public InputStream toStream() { + return new ByteArrayInputStream(this.data); + } + + /** + * Provides {@link Mono} of {@link InputStream} for the data represented by this {@link BinaryData} object. + * + * @return {@link InputStream} representation of the {@link BinaryData}. + */ + public Mono toStreamAsync() { + return Mono.fromCallable(() -> toStream()); + } + + /** + * Create {@link BinaryData} instance with given {@link InputStream} as source of data. The {@link InputStream} is + * not closed by this function. + * + *

Create an instance from InputStream

+ * {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#Stream} + * + * @param inputStream to read bytes from. + * @throws UncheckedIOException If any error in reading from {@link InputStream}. + * @throws NullPointerException if {@code inputStream} is null. + * @return {@link BinaryData} representing the binary data. + */ + public static BinaryData fromStream(InputStream inputStream) { + Objects.requireNonNull(inputStream, "'inputStream' cannot be null."); + + final int bufferSize = 1024; + try { + ByteArrayOutputStream dataOutputBuffer = new ByteArrayOutputStream(); + int nRead; + byte[] data = new byte[bufferSize]; + while ((nRead = inputStream.read(data, 0, data.length)) != -1) { + dataOutputBuffer.write(data, 0, nRead); + } + + return fromBytes(dataOutputBuffer.toByteArray()); + } catch (IOException ex) { + throw LOGGER.logExceptionAsError(new UncheckedIOException(ex)); + } + } + + /** + * Asynchronously create {@link BinaryData} instance with given {@link InputStream} as source of data. The + * {@link InputStream} is not closed by this function. + * + * @param inputStream to read bytes from. + * @throws NullPointerException if {@code inputStream} is null. + * @return {@link Mono} of {@link BinaryData} representing the binary data. + */ + public static Mono fromStreamAsync(InputStream inputStream) { + Objects.requireNonNull(inputStream, "'inputStream' cannot be null."); + + return Mono.fromCallable(() -> fromStream(inputStream)); + } + + /** + * Create {@link BinaryData} instance with given {@link Flux} of {@link ByteBuffer} as source of data. It will + * collect all the bytes from {@link ByteBuffer} into {@link BinaryData}. + * + *

Create an instance from String

+ * {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#Flux} + * + * @param data to use. + * @throws NullPointerException if {@code inputStream} is null. + * @return {@link Mono} of {@link BinaryData} representing binary data. + */ + public static Mono fromFlux(Flux data) { + if (Objects.isNull(data)) { + return monoError(LOGGER, new NullPointerException("'data' cannot be null.")); + } + + return FluxUtil.collectBytesInByteBufferStream(data) + .flatMap(bytes -> Mono.just(fromBytes(bytes))); + } + + /** + * Create {@link BinaryData} instance with given data and character set. + * + *

Create an instance from String

+ * {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#String} + * + * @param data to use. + * @param charSet to use. + * @throws NullPointerException if {@code inputStream} is null. + * @return {@link BinaryData} representing the binary data. + */ + public static BinaryData fromString(String data, Charset charSet) { + Objects.requireNonNull(data, "'data' cannot be null."); + + return new BinaryData(data.getBytes(charSet)); + } + + /** + * Create {@link BinaryData} instance with given data. The {@link String} is converted into bytes using + * {@link StandardCharsets#UTF_8} character set. + * + * @param data to use. + * @throws NullPointerException if {@code inputStream} is null. + * @return {@link BinaryData} representing binary data. + */ + public static BinaryData fromString(String data) { + Objects.requireNonNull(data, "'data' cannot be null."); + + return new BinaryData(data.getBytes(StandardCharsets.UTF_8)); + } + + /** + * Create {@link BinaryData} instance with given byte array data. + * + * @param data to use. + * @return {@link BinaryData} representing the binary data. + */ + public static BinaryData fromBytes(byte[] data) { + return new BinaryData(data); + } + + /** + * Serialize the given {@link Object} into {@link BinaryData} using the provided {@link ObjectSerializer}. + * + * @param data The {@link Object} which needs to be serialized into bytes. + * @param serializer to use for serializing the object. + * @throws NullPointerException if {@code inputStream} or {@code serializer} is null. + * @return {@link BinaryData} representing binary data. + */ + public static BinaryData fromObject(Object data, ObjectSerializer serializer) { + Objects.requireNonNull(data, "'data' cannot be null."); + Objects.requireNonNull(serializer, "'serializer' cannot be null."); + + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + serializer.serialize(outputStream, data); + return new BinaryData(outputStream.toByteArray()); + } + + /** + * Serialize the given {@link Object} into {@link Mono} {@link BinaryData} using the provided + * {@link ObjectSerializer}. + * + * @param data The {@link Object} which needs to be serialized into bytes. + * @param serializer to use for serializing the object. + * @throws NullPointerException if {@code inputStream} or {@code serializer} is null. + * @return {@link Mono} of {@link BinaryData} representing the binary data. + */ + public static Mono fromObjectAsync(Object data, ObjectSerializer serializer) { + Objects.requireNonNull(data, "'data' cannot be null."); + Objects.requireNonNull(serializer, "'serializer' cannot be null."); + + return Mono.fromCallable(() -> fromObject(data, serializer)); + + } + + /** + * Provides byte array representation of this {@link BinaryData} object. + * + * @return byte array representation of the the data. + */ + public byte[] toBytes() { + return Arrays.copyOf(this.data, this.data.length); + } + + /** + * Provides {@link String} representation of this {@link BinaryData} object. The bytes are converted into + * {@link String} using {@link StandardCharsets#UTF_8} character set. + * + * @return {@link String} representation of the data. + */ + public String toString() { + return new String(this.data, StandardCharsets.UTF_8); + } + + /** + * Provides {@link String} representation of this {@link BinaryData} object given a character set. + * + * @param charSet to use to convert bytes into {@link String}. + * @return {@link String} representation of the the binary data. + */ + public String toString(Charset charSet) { + return new String(this.data, charSet); + } + + /** + * Deserialize the bytes into the {@link Object} of given type by applying the provided {@link ObjectSerializer} on + * the data. + * + * @param clazz representing the type of the Object. + * @param serializer to use deserialize data into type. + * @param Generic type that the data is deserialized into. + * @return The {@link Object} of given type after deserializing the bytes. + */ + public T toObject(Class clazz, ObjectSerializer serializer) { + Objects.requireNonNull(clazz, "'clazz' cannot be null."); + Objects.requireNonNull(serializer, "'serializer' cannot be null."); + + TypeReference ref = TypeReference.createInstance(clazz); + InputStream jsonStream = new ByteArrayInputStream(this.data); + return serializer.deserialize(jsonStream, ref); + } + + /** + * Return a {@link Mono} by deserialize the bytes into the {@link Object} of given type after applying the provided + * {@link ObjectSerializer} on the {@link BinaryData}. + * + *

Gets the specified object

+ * {@codesnippet com.azure.core.experimental.util.BinaryDocument.to#ObjectAsync} + * + * @param clazz representing the type of the Object. + * @param serializer to use deserialize data into type. + * @param Generic type that the data is deserialized into. + * @throws NullPointerException if {@code clazz} or {@code serializer} is null. + * @return The {@link Object} of given type after deserializing the bytes. + */ + public Mono toObjectAsync(Class clazz, ObjectSerializer serializer) { + return Mono.fromCallable(() -> toObject(clazz, serializer)); + } +} diff --git a/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/util/package-info.java b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/util/package-info.java new file mode 100644 index 0000000000000..3cce26af06e17 --- /dev/null +++ b/sdk/core/azure-core-experimental/src/main/java/com/azure/core/experimental/util/package-info.java @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/** + * Package containing utilities classes. + */ +package com.azure.core.experimental.util; diff --git a/sdk/core/azure-core-experimental/src/main/java/module-info.java b/sdk/core/azure-core-experimental/src/main/java/module-info.java index 9a7ddea4ef506..346e9ab2f1018 100644 --- a/sdk/core/azure-core-experimental/src/main/java/module-info.java +++ b/sdk/core/azure-core-experimental/src/main/java/module-info.java @@ -7,6 +7,7 @@ exports com.azure.core.experimental.jsonpatch; exports com.azure.core.experimental.serializer; exports com.azure.core.experimental.spatial; + exports com.azure.core.experimental.util; opens com.azure.core.experimental.jsonpatch to com.fasterxml.jackson.databind; diff --git a/sdk/core/azure-core-experimental/src/samples/java/com/azure/core/experimental/util/BinaryDateJavaDocCodeSnippet.java b/sdk/core/azure-core-experimental/src/samples/java/com/azure/core/experimental/util/BinaryDateJavaDocCodeSnippet.java new file mode 100644 index 0000000000000..d4999b654cf24 --- /dev/null +++ b/sdk/core/azure-core-experimental/src/samples/java/com/azure/core/experimental/util/BinaryDateJavaDocCodeSnippet.java @@ -0,0 +1,195 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.core.experimental.util; + +import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.serializer.JsonSerializer; +import com.azure.core.util.serializer.ObjectSerializer; +import com.azure.core.util.serializer.TypeReference; +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; +import reactor.core.Disposable; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +/** + * Codesnippets for {@link BinaryData}. + */ +public class BinaryDateJavaDocCodeSnippet { + + /** + * Codesnippets for {@link BinaryData#fromBytes(byte[])}. + */ + public void createFromBytes() { + // BEGIN: com.azure.core.experimental.util.BinaryDocument.from#bytes + final byte[] data = "Some Data".getBytes(StandardCharsets.UTF_8); + BinaryData binaryData = BinaryData.fromBytes(data); + System.out.println(new String(binaryData.toBytes(), StandardCharsets.UTF_8)); + // END: com.azure.core.experimental.util.BinaryDocument.from#bytes + } + + /** + * Codesnippets for {@link BinaryData#fromString(String)}. + */ + public void createFromString() { + // BEGIN: com.azure.core.experimental.util.BinaryDocument.from#String + final String data = "Some Data"; + // Following will use default character set as StandardCharsets.UTF_8 + BinaryData binaryData = BinaryData.fromString(data); + System.out.println(binaryData.toString()); + // END: com.azure.core.experimental.util.BinaryDocument.from#String + } + + /** + * Codesnippets for {@link BinaryData#fromStream(InputStream)}. + */ + public void createFromStream() { + // BEGIN: com.azure.core.experimental.util.BinaryDocument.from#Stream + final byte[] data = "Some Data".getBytes(StandardCharsets.UTF_8); + BinaryData binaryData = BinaryData.fromStream(new ByteArrayInputStream(data)); + System.out.println(binaryData.toString()); + // END: com.azure.core.experimental.util.BinaryDocument.from#Stream + } + + /** + * Codesnippets for {@link BinaryData#fromStream(InputStream)}. + */ + public void createFromFlux() throws InterruptedException { + // BEGIN: com.azure.core.experimental.util.BinaryDocument.from#Flux + final byte[] data = "Some Data".getBytes(StandardCharsets.UTF_8); + final Flux dataFlux = Flux.just(ByteBuffer.wrap(data)); + + Mono binaryDataMono = BinaryData.fromFlux(dataFlux); + + // Lets print the value of BinaryData + Disposable subscriber = binaryDataMono + .map(binaryData -> { + System.out.println(binaryData.toString()); + return true; + }) + .subscribe(); + + // So that your program wait for above subscribe to complete. + TimeUnit.SECONDS.sleep(5); + subscriber.dispose(); + // END: com.azure.core.experimental.util.BinaryDocument.from#Flux + } + + /** + * Codesnippets for {@link BinaryData#toStream()}. + */ + public void toStream() throws IOException { + // BEGIN: com.azure.core.experimental.util.BinaryDocument.to#Stream + final byte[] data = "Some Data".getBytes(StandardCharsets.UTF_8); + BinaryData binaryData = BinaryData.fromStream(new ByteArrayInputStream(data)); + final byte[] bytes = new byte[data.length]; + (binaryData.toStream()).read(bytes, 0, data.length); + System.out.println(new String(bytes)); + // END: com.azure.core.experimental.util.BinaryDocument.to#Stream + } + + /** + * Codesnippets for {@link BinaryData#toObjectAsync(Class, ObjectSerializer)}. + */ + public void createToObjectAsync() throws InterruptedException { + // BEGIN: com.azure.core.experimental.util.BinaryDocument.to#ObjectAsync + // Lets say we have Person object which could be serialized into json. + class Person { + @JsonProperty + private String name; + + @JsonSetter + public Person setName(String name) { + this.name = name; + return this; + } + + @JsonGetter + public String getName() { + return name; + } + } + final Person data = new Person().setName("John"); + + // Ensure your classpath have the Serializer to use to serialize object. For example you can use one of + // following library. + // https://mvnrepository.com/artifact/com.azure/azure-core-serializer-json-jackson or + // https://mvnrepository.com/artifact/com.azure/azure-core-serializer-json-gson + + final ObjectSerializer serializer = + new MyJsonSerializer(); // Replace this with your Serializer + BinaryData binaryData = BinaryData.fromObject(data, serializer); + + // Lets print the value of BinaryData + Disposable subscriber = binaryData + .toObjectAsync(Person.class, serializer) + .map(person -> { + System.out.println(person.getName()); + return true; + }) + .subscribe(); + + // So that your program wait for above subscribe to complete. + TimeUnit.SECONDS.sleep(5); + subscriber.dispose(); + // END: com.azure.core.experimental.util.BinaryDocument.to#ObjectAsync + } + + public static class MyJsonSerializer implements JsonSerializer { + private final ClientLogger logger = new ClientLogger(BinaryDataTest.MyJsonSerializer.class); + private final ObjectMapper mapper; + private final TypeFactory typeFactory; + + public MyJsonSerializer() { + this.mapper = new ObjectMapper(); + this.typeFactory = mapper.getTypeFactory(); + } + + @Override + public T deserialize(InputStream stream, TypeReference typeReference) { + if (stream == null) { + return null; + } + + try { + return mapper.readValue(stream, typeFactory.constructType(typeReference.getJavaType())); + } catch (IOException ex) { + throw logger.logExceptionAsError(new UncheckedIOException(ex)); + } + } + + @Override + public Mono deserializeAsync(InputStream stream, TypeReference typeReference) { + return Mono.fromCallable(() -> deserialize(stream, typeReference)); + } + + + @Override + public void serialize(OutputStream stream, Object value) { + try { + mapper.writeValue(stream, value); + } catch (IOException ex) { + throw logger.logExceptionAsError(new UncheckedIOException(ex)); + } + } + + @Override + public Mono serializeAsync(OutputStream stream, Object value) { + return Mono.fromRunnable(() -> serialize(stream, value)); + } + } +} + diff --git a/sdk/core/azure-core-experimental/src/test/java/com/azure/core/experimental/util/BinaryDataTest.java b/sdk/core/azure-core-experimental/src/test/java/com/azure/core/experimental/util/BinaryDataTest.java new file mode 100644 index 0000000000000..e0afe19e16d3a --- /dev/null +++ b/sdk/core/azure-core-experimental/src/test/java/com/azure/core/experimental/util/BinaryDataTest.java @@ -0,0 +1,235 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.core.experimental.util; + +import com.azure.core.util.logging.ClientLogger; +import com.azure.core.util.serializer.JsonSerializer; +import com.azure.core.util.serializer.ObjectSerializer; +import com.azure.core.util.serializer.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Test class for {@link BinaryData}. + */ +public class BinaryDataTest { + private static final ObjectSerializer DEFAULT_SERIALIZER = new MyJsonSerializer(); + + @Test + public void fromCustomObject() { + // Arrange + final Person actualValue = new Person().setName("John Doe").setAge(50); + final Person expectedValue = new Person().setName("John Doe").setAge(50); + + // Act + final BinaryData data = BinaryData.fromObject(actualValue, DEFAULT_SERIALIZER); + + // Assert + assertEquals(expectedValue, data.toObject(expectedValue.getClass(), DEFAULT_SERIALIZER)); + } + + @Test + public void fromDouble() { + // Arrange + final Double actualValue = Double.valueOf("10.1"); + final Double expectedValue = Double.valueOf("10.1"); + + // Act + final BinaryData data = BinaryData.fromObject(actualValue, DEFAULT_SERIALIZER); + + // Assert + assertEquals(expectedValue, data.toObject(expectedValue.getClass(), DEFAULT_SERIALIZER)); + } + + @Test + public void anyTypeToByteArray() { + // Assert + final Person actualValue = new Person().setName("John Doe").setAge(50); + final byte[] expectedValue = "{\"name\":\"John Doe\",\"age\":50}".getBytes(StandardCharsets.UTF_8); + + // Act + final BinaryData data = BinaryData.fromObject(actualValue, DEFAULT_SERIALIZER); + + // Assert + assertArrayEquals(expectedValue, data.toBytes()); + } + + @Test + public void createFromString() { + // Arrange + final String expected = "Doe"; + + // Act + final BinaryData data = BinaryData.fromString(expected); + + // Assert + assertArrayEquals(expected.getBytes(), data.toBytes()); + assertEquals(expected, data.toString()); + } + + @Test + public void createFromStringCharSet() { + // Arrange + final String expected = "Doe"; + + // Act + final BinaryData data = BinaryData.fromString(expected, StandardCharsets.UTF_8); + + // Assert + assertArrayEquals(expected.getBytes(), data.toBytes()); + assertEquals(expected, data.toString(StandardCharsets.UTF_8)); + } + + @Test + public void createFromByteArray() { + // Arrange + final byte[] expected = "Doe".getBytes(StandardCharsets.UTF_8); + + // Act + final BinaryData data = new BinaryData(expected); + + // Assert + assertArrayEquals(expected, data.toBytes()); + } + + @Test + public void createFromStream() throws IOException { + // Arrange + final byte[] expected = "Doe".getBytes(StandardCharsets.UTF_8); + final byte[] actual = new byte[expected.length]; + + // Act + BinaryData data = BinaryData.fromStream(new ByteArrayInputStream(expected)); + (data.toStream()).read(actual, 0, expected.length); + + // Assert + assertArrayEquals(expected, data.toBytes()); + assertArrayEquals(expected, actual); + } + + @Test + public void createFromFlux() { + // Arrange + final byte[] data = "Doe".getBytes(StandardCharsets.UTF_8); + final Flux expectedFlux = Flux.just(ByteBuffer.wrap(data), ByteBuffer.wrap(data)); + final byte[] expected = "DoeDoe".getBytes(StandardCharsets.UTF_8); + + // Act & Assert + StepVerifier.create(BinaryData.fromFlux(expectedFlux)) + .assertNext(actual -> { + Assertions.assertArrayEquals(expected, actual.toBytes()); + }) + .verifyComplete(); + } + + @Test + public void createFromStreamAsync() throws IOException { + // Arrange + final byte[] expected = "Doe".getBytes(StandardCharsets.UTF_8); + + // Act & Assert + StepVerifier.create(BinaryData.fromStreamAsync(new ByteArrayInputStream(expected))) + .assertNext(actual -> { + Assertions.assertArrayEquals(expected, actual.toBytes()); + }) + .verifyComplete(); + } + + @Test + public void createToStreamAsync() { + // Arrange + final byte[] expected = "Doe".getBytes(StandardCharsets.UTF_8); + final BinaryData actual = BinaryData.fromStreamAsync(new ByteArrayInputStream(expected)).block(); + // Act & Assert + StepVerifier.create(actual.toStreamAsync()) + .assertNext(inutStream -> { + byte[] actualBytes = new byte[expected.length]; + + // Act + try { + inutStream.read(actualBytes, 0, expected.length); + } catch (IOException e) { + e.printStackTrace(); + } + Assertions.assertArrayEquals(expected, actualBytes); + }) + .verifyComplete(); + } + + @Test + public void createFromObjectAsync() { + // Arrange + final Person expected = new Person().setName("Jon").setAge(50); + final BinaryData expectedBinaryData = BinaryData.fromObjectAsync(expected, DEFAULT_SERIALIZER).block(); + + // Act & Assert + StepVerifier.create(expectedBinaryData.toObjectAsync(Person.class, DEFAULT_SERIALIZER)) + .assertNext(actual -> { + System.out.println(actual.getName()); + System.out.println(actual.getAge()); + Assertions.assertEquals(expected, actual); + }) + .verifyComplete(); + } + + public static class MyJsonSerializer implements JsonSerializer { + private final ClientLogger logger = new ClientLogger(MyJsonSerializer.class); + private final ObjectMapper mapper; + private final TypeFactory typeFactory; + + public MyJsonSerializer() { + this.mapper = new ObjectMapper(); + this.typeFactory = mapper.getTypeFactory(); + } + + @Override + public T deserialize(InputStream stream, TypeReference typeReference) { + if (stream == null) { + return null; + } + + try { + return mapper.readValue(stream, typeFactory.constructType(typeReference.getJavaType())); + } catch (IOException ex) { + throw logger.logExceptionAsError(new UncheckedIOException(ex)); + } + } + + @Override + public Mono deserializeAsync(InputStream stream, TypeReference typeReference) { + return Mono.fromCallable(() -> deserialize(stream, typeReference)); + } + + + @Override + public void serialize(OutputStream stream, Object value) { + try { + mapper.writeValue(stream, value); + } catch (IOException ex) { + throw logger.logExceptionAsError(new UncheckedIOException(ex)); + } + } + + @Override + public Mono serializeAsync(OutputStream stream, Object value) { + return Mono.fromRunnable(() -> serialize(stream, value)); + } + } +} diff --git a/sdk/core/azure-core-experimental/src/test/java/com/azure/core/experimental/util/Person.java b/sdk/core/azure-core-experimental/src/test/java/com/azure/core/experimental/util/Person.java new file mode 100644 index 0000000000000..4c909f39a3a87 --- /dev/null +++ b/sdk/core/azure-core-experimental/src/test/java/com/azure/core/experimental/util/Person.java @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.core.experimental.util; + +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; + +import java.util.Objects; + +public class Person { + @JsonProperty + private String name; + + @JsonProperty + private int age; + + + public Person() { + } + + @JsonSetter + public Person setName(String name) { + this.name = name; + return this; + } + + @JsonGetter + public String getName() { + return name; + } + + @JsonSetter + public Person setAge(int age) { + this.age = age; + return this; + } + + @JsonGetter + public int getAge() { + return age; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (other == null || getClass() != other.getClass()) { + return false; + } + + Person person = (Person) other; + + return age == person.age && Objects.equals(name, person.name); + } + + @Override + public int hashCode() { + return Objects.hash(name, age); + } +}