Skip to content

Commit

Permalink
Sb BinaryData - Binary representation of data from different sources (#…
Browse files Browse the repository at this point in the history
…15920)

* Adding BinaryData type to represent binary .
  • Loading branch information
hemanttanwar authored Oct 6, 2020
1 parent 07c4003 commit 4274680
Show file tree
Hide file tree
Showing 9 changed files with 797 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@
<suppress checks="com.azure.tools.checkstyle.checks.GoodLoggingCheck" files="com.azure.core.serializer.avro.apache.ApacheAvroSerializerProvider.java"/>
<suppress checks="com.azure.tools.checkstyle.checks.GoodLoggingCheck" files="com.azure.core.serializer.json.gson.JsonNodeUtils.java"/>
<suppress checks="com.azure.tools.checkstyle.checks.GoodLoggingCheck" files="com.azure.core.serializer.json.jackson.JsonNodeUtils.java"/>
<suppress checks="com.azure.tools.checkstyle.checks.GoodLoggingCheck" files="com.azure.core.experimental.util.BinaryData.java"/>

<!-- Azure Core Test -->
<suppress checks="com.azure.tools.checkstyle.checks.GoodLoggingCheck" files=".*[/\\]core[/\\]test[/\\].*\.java"/>
Expand Down Expand Up @@ -550,7 +551,7 @@
<!-- TODO: Fix with https://github.com/Azure/azure-sdk-for-java/issues#6716 Method name should follow a common vocabulary. -->
<suppress checks="com.azure.tools.checkstyle.checks.ServiceClientCheck" files="com.azure.communication.administration.PhoneNumberClient.java"/>
<suppress checks="com.azure.tools.checkstyle.checks.ServiceClientCheck" files="com.azure.communication.administration.PhoneNumberAsyncClient.java"/>

<!-- Checkstyle suppressions for azure-spring AADB2C package name-->
<suppress checks="PackageName" files="com.microsoft.azure.spring.autoconfigure.*|com.azure.spring.*"/>

Expand Down
2 changes: 2 additions & 0 deletions sdk/core/azure-core-experimental/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions sdk/core/azure-core-experimental/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@
<version>5.6.2</version> <!-- {x-version-update;org.junit.jupiter:junit-jupiter-params;external_dependency} -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<version>3.3.9.RELEASE</version> <!-- {x-version-update;io.projectreactor:reactor-test;external_dependency} -->
<scope>test</scope>
</dependency>
</dependencies>

<profiles>
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* It also provides a way to serialize and deserialize an {@link Object} into {@link BinaryData} given an
* {@link ObjectSerializer}. Code samples are explained below.
*
* <p><strong>Create an instance from Bytes</strong></p>
* {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#bytes}
*
* <p><strong>Create an instance from String</strong></p>
* {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#String}
*
* <p><strong>Create an instance from InputStream</strong></p>
* {@codesnippet com.azure.core.experimental.util.BinaryDocument.from#Stream}
*
* <p><strong>Get an Object from {@link BinaryData}</strong></p>
* {@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.
*
* <p><strong>Get InputStream from BinaryData</strong></p>
* {@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<InputStream> 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.
*
* <p><strong>Create an instance from InputStream</strong></p>
* {@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<BinaryData> 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}.
*
* <p><strong>Create an instance from String</strong></p>
* {@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<BinaryData> fromFlux(Flux<ByteBuffer> 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.
*
* <p><strong>Create an instance from String</strong></p>
* {@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<BinaryData> 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 <T> Generic type that the data is deserialized into.
* @return The {@link Object} of given type after deserializing the bytes.
*/
public <T> T toObject(Class<T> clazz, ObjectSerializer serializer) {
Objects.requireNonNull(clazz, "'clazz' cannot be null.");
Objects.requireNonNull(serializer, "'serializer' cannot be null.");

TypeReference<T> 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}.
*
* <p><strong>Gets the specified object</strong></p>
* {@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 <T> 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 <T> Mono<T> toObjectAsync(Class<T> clazz, ObjectSerializer serializer) {
return Mono.fromCallable(() -> toObject(clazz, serializer));
}
}
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Loading

0 comments on commit 4274680

Please sign in to comment.