1 /* 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"). 5 * You may not use this file except in compliance with the License. 6 * A copy of the License is located at 7 * 8 * http://aws.amazon.com/apache2.0 9 * 10 * or in the "license" file accompanying this file. This file is distributed 11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 * express or implied. See the License for the specific language governing 13 * permissions and limitations under the License. 14 */ 15 16 package software.amazon.awssdk.core; 17 18 import static java.nio.charset.StandardCharsets.UTF_8; 19 20 import java.io.ByteArrayInputStream; 21 import java.io.InputStream; 22 import java.io.UncheckedIOException; 23 import java.nio.ByteBuffer; 24 import java.nio.charset.CharacterCodingException; 25 import java.nio.charset.Charset; 26 import java.util.Arrays; 27 import software.amazon.awssdk.annotations.SdkInternalApi; 28 import software.amazon.awssdk.annotations.SdkPublicApi; 29 import software.amazon.awssdk.http.ContentStreamProvider; 30 import software.amazon.awssdk.utils.StringUtils; 31 import software.amazon.awssdk.utils.Validate; 32 33 /** 34 * A base class for {@link SdkBytes} and {@link ResponseBytes} that enables retrieving an underlying byte array as multiple 35 * different types, like a byte buffer (via {@link #asByteBuffer()}, or a string (via {@link #asUtf8String()}. 36 */ 37 @SdkPublicApi 38 public abstract class BytesWrapper { 39 private final byte[] bytes; 40 41 // Needed for serialization 42 @SdkInternalApi BytesWrapper()43 BytesWrapper() { 44 this(new byte[0]); 45 } 46 47 @SdkInternalApi BytesWrapper(byte[] bytes)48 BytesWrapper(byte[] bytes) { 49 this.bytes = Validate.paramNotNull(bytes, "bytes"); 50 } 51 52 /** 53 * @return The output as a read-only byte buffer. 54 */ asByteBuffer()55 public final ByteBuffer asByteBuffer() { 56 return ByteBuffer.wrap(bytes).asReadOnlyBuffer(); 57 } 58 59 /** 60 * @return A copy of the output as a byte array. 61 * @see #asByteBuffer() to prevent creating an additional array copy. 62 */ asByteArray()63 public final byte[] asByteArray() { 64 return Arrays.copyOf(bytes, bytes.length); 65 } 66 67 /** 68 * @return The output as a byte array. This <b>does not</b> create a copy of the underlying byte array. This introduces 69 * concurrency risks, allowing: (1) the caller to modify the byte array stored in this object implementation AND 70 * (2) the original creator of this object, if they created it using the unsafe method. 71 * 72 * <p>Consider using {@link #asByteBuffer()}, which is a safer method to avoid an additional array copy because it does not 73 * provide a way to modify the underlying buffer. As the method name implies, this is unsafe. If you're not sure, don't use 74 * this. The only guarantees given to the user of this method is that the SDK itself won't modify the underlying byte 75 * array.</p> 76 * 77 * @see #asByteBuffer() to prevent creating an additional array copy safely. 78 */ asByteArrayUnsafe()79 public final byte[] asByteArrayUnsafe() { 80 return bytes; 81 } 82 83 /** 84 * Retrieve the output as a string. 85 * 86 * @param charset The charset of the string. 87 * @return The output as a string. 88 * @throws UncheckedIOException with a {@link CharacterCodingException} as the cause if the bytes cannot be encoded using the 89 * provided charset 90 */ asString(Charset charset)91 public final String asString(Charset charset) throws UncheckedIOException { 92 return StringUtils.fromBytes(bytes, charset); 93 } 94 95 /** 96 * @return The output as a utf-8 encoded string. 97 * @throws UncheckedIOException with a {@link CharacterCodingException} as the cause if the bytes cannot be encoded as UTF-8. 98 */ asUtf8String()99 public final String asUtf8String() throws UncheckedIOException { 100 return asString(UTF_8); 101 } 102 103 /** 104 * @return The output as an input stream. This stream will not need to be closed. 105 */ asInputStream()106 public final InputStream asInputStream() { 107 return new ByteArrayInputStream(bytes); 108 } 109 110 /** 111 * @return The output as a {@link ContentStreamProvider}. 112 */ asContentStreamProvider()113 public final ContentStreamProvider asContentStreamProvider() { 114 return this::asInputStream; 115 } 116 117 @Override equals(Object o)118 public boolean equals(Object o) { 119 if (this == o) { 120 return true; 121 } 122 if (o == null || getClass() != o.getClass()) { 123 return false; 124 } 125 126 BytesWrapper sdkBytes = (BytesWrapper) o; 127 128 return Arrays.equals(bytes, sdkBytes.bytes); 129 } 130 131 @Override hashCode()132 public int hashCode() { 133 return Arrays.hashCode(bytes); 134 } 135 } 136