mirror of https://github.com/apache/jclouds.git
JCLOUDS-457: Clean up for the validators and the TreeHash class.
Now the validators use checkArgument instead of throwing an exception by calling an static method. There were some missing validators on the Async client, these were fixed too. Lastly, TreeHash inner class was removed. Now the factory methods are in the TreeHash class.
This commit is contained in:
parent
248c855e48
commit
76ea768cb8
|
@ -79,7 +79,7 @@ public interface GlacierAsyncClient extends Closeable {
|
|||
@Named("CreateVault")
|
||||
@PUT
|
||||
@Path("/-/vaults/{vault}")
|
||||
ListenableFuture<URI> createVault(@PathParam("vault") @ParamValidators(VaultNameValidator.class) String vaultName);
|
||||
ListenableFuture<URI> createVault(@ParamValidators(VaultNameValidator.class) @PathParam("vault") String vaultName);
|
||||
|
||||
/**
|
||||
* @see GlacierClient#deleteVaultIfEmpty
|
||||
|
@ -88,7 +88,7 @@ public interface GlacierAsyncClient extends Closeable {
|
|||
@DELETE
|
||||
@Path("/-/vaults/{vault}")
|
||||
@Fallback(FalseOnIllegalArgumentException.class)
|
||||
ListenableFuture<Boolean> deleteVault(@PathParam("vault") String vaultName);
|
||||
ListenableFuture<Boolean> deleteVault(@ParamValidators(VaultNameValidator.class) @PathParam("vault") String vaultName);
|
||||
|
||||
/**
|
||||
* @see GlacierClient#describeVault
|
||||
|
@ -99,7 +99,7 @@ public interface GlacierAsyncClient extends Closeable {
|
|||
@ResponseParser(ParseVaultMetadataFromHttpContent.class)
|
||||
@Fallback(NullOnNotFoundOr404.class)
|
||||
ListenableFuture<VaultMetadata> describeVault(
|
||||
@PathParam("vault") @ParamValidators(VaultNameValidator.class) String vaultName);
|
||||
@ParamValidators(VaultNameValidator.class) @PathParam("vault") String vaultName);
|
||||
|
||||
/**
|
||||
* @see GlacierClient#listVaults(PaginationOptions)
|
||||
|
@ -127,7 +127,7 @@ public interface GlacierAsyncClient extends Closeable {
|
|||
@Path("/-/vaults/{vault}/archives")
|
||||
@ResponseParser(ParseArchiveIdHeader.class)
|
||||
ListenableFuture<String> uploadArchive(
|
||||
@PathParam("vault") String vaultName,
|
||||
@ParamValidators(VaultNameValidator.class) @PathParam("vault") String vaultName,
|
||||
@ParamValidators(PayloadValidator.class) @BinderParam(BindHashesToHeaders.class) Payload payload,
|
||||
@ParamValidators(DescriptionValidator.class) @BinderParam(BindDescriptionToHeaders.class) String description);
|
||||
|
||||
|
@ -139,7 +139,7 @@ public interface GlacierAsyncClient extends Closeable {
|
|||
@Path("/-/vaults/{vault}/archives")
|
||||
@ResponseParser(ParseArchiveIdHeader.class)
|
||||
ListenableFuture<String> uploadArchive(
|
||||
@PathParam("vault") String vaultName,
|
||||
@ParamValidators(VaultNameValidator.class) @PathParam("vault") String vaultName,
|
||||
@ParamValidators(PayloadValidator.class) @BinderParam(BindHashesToHeaders.class) Payload payload);
|
||||
|
||||
/**
|
||||
|
@ -149,7 +149,7 @@ public interface GlacierAsyncClient extends Closeable {
|
|||
@DELETE
|
||||
@Path("/-/vaults/{vault}/archives/{archive}")
|
||||
ListenableFuture<Boolean> deleteArchive(
|
||||
@PathParam("vault") String vaultName,
|
||||
@ParamValidators(VaultNameValidator.class) @PathParam("vault") String vaultName,
|
||||
@PathParam("archive") String archiveId);
|
||||
|
||||
/**
|
||||
|
@ -182,7 +182,8 @@ public interface GlacierAsyncClient extends Closeable {
|
|||
@PUT
|
||||
@Path("/-/vaults/{vault}/multipart-uploads/{uploadId}")
|
||||
@ResponseParser(ParseMultipartUploadTreeHashHeader.class)
|
||||
ListenableFuture<String> uploadPart(@PathParam("vault") String vaultName,
|
||||
ListenableFuture<String> uploadPart(
|
||||
@ParamValidators(VaultNameValidator.class) @PathParam("vault") String vaultName,
|
||||
@PathParam("uploadId") String uploadId,
|
||||
@BinderParam(BindContentRangeToHeaders.class) ContentRange range,
|
||||
@ParamValidators(PayloadValidator.class) @BinderParam(BindHashesToHeaders.class) Payload payload);
|
||||
|
|
|
@ -35,7 +35,7 @@ public class BindHashesToHeaders implements Binder {
|
|||
|
||||
private HttpRequest addChecksumHeaders(HttpRequest request, Payload payload) {
|
||||
try {
|
||||
TreeHash hash = TreeHash.Hasher.buildTreeHashFromPayload(payload);
|
||||
TreeHash hash = TreeHash.buildTreeHashFromPayload(payload);
|
||||
request = request.toBuilder()
|
||||
.addHeader(GlacierHeaders.LINEAR_HASH, hash.getLinearHash().toString())
|
||||
.addHeader(GlacierHeaders.TREE_HASH, hash.getTreeHash().toString())
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.jclouds.glacier.predicates.validators;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
|
||||
import org.jclouds.predicates.Validator;
|
||||
|
@ -37,17 +38,10 @@ public final class DescriptionValidator extends Validator<String> {
|
|||
public void validate(String description) {
|
||||
if (isNullOrEmpty(description))
|
||||
return;
|
||||
if (description.length() > MAX_DESC_LENGTH)
|
||||
throw exception("Description can't be longer than " + MAX_DESC_LENGTH + " characters" + " but was " + description.length());
|
||||
if (!DESCRIPTION_ACCEPTABLE_RANGE.matchesAllOf(description))
|
||||
throw exception("Description should have ASCII values between 32 and 126.");
|
||||
}
|
||||
|
||||
protected static IllegalArgumentException exception(String reason) {
|
||||
return new IllegalArgumentException(
|
||||
String.format(
|
||||
"Description doesn't match Glacier archive description rules. "
|
||||
+ "Reason: %s. For more info, please refer to http://docs.aws.amazon.com/amazonglacier/latest/dev/api-archive-post.html.",
|
||||
reason));
|
||||
checkArgument(description.length() <= MAX_DESC_LENGTH,
|
||||
"Description can't be longer than %d characters but was %d",
|
||||
MAX_DESC_LENGTH, description.length());
|
||||
checkArgument(DESCRIPTION_ACCEPTABLE_RANGE.matchesAllOf(description),
|
||||
"Description should have ASCII values between 32 and 126.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,13 +16,17 @@
|
|||
*/
|
||||
package org.jclouds.glacier.predicates.validators;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import org.jclouds.predicates.Validator;
|
||||
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Validates the part size parameter used when initiating multipart uploads.
|
||||
*/
|
||||
@Singleton
|
||||
public final class PartSizeValidator extends Validator<Long> {
|
||||
private static final int MIN_PART_SIZE = 1;
|
||||
private static final int MAX_PART_SIZE = 4096;
|
||||
|
@ -30,15 +34,7 @@ public final class PartSizeValidator extends Validator<Long> {
|
|||
@Override
|
||||
public void validate(Long partSizeInMB) throws IllegalArgumentException {
|
||||
checkNotNull(partSizeInMB, "partSizeInMB");
|
||||
if (partSizeInMB < MIN_PART_SIZE || partSizeInMB > MAX_PART_SIZE || (partSizeInMB & (partSizeInMB - 1)) != 0)
|
||||
throw exception(partSizeInMB, "partSizeInMB must be a power of 2 between 1 and 4096.");
|
||||
}
|
||||
|
||||
protected static IllegalArgumentException exception(Long size, String reason) {
|
||||
return new IllegalArgumentException(
|
||||
String.format(
|
||||
"Part size '%s' doesn't match Glacier Multipart upload rules. "
|
||||
+ "Reason: %s. For more info, please refer to http://http://docs.aws.amazon.com/amazonglacier/latest/dev/api-multipart-initiate-upload.html.",
|
||||
size, reason));
|
||||
checkArgument(!(partSizeInMB < MIN_PART_SIZE || partSizeInMB > MAX_PART_SIZE || Long.bitCount(partSizeInMB) != 1),
|
||||
"partSizeInMB must be a power of 2 between 1 and 4096.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
*/
|
||||
package org.jclouds.glacier.predicates.validators;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.predicates.Validator;
|
||||
|
||||
|
@ -31,19 +34,9 @@ public final class PayloadValidator extends Validator<Payload> {
|
|||
|
||||
@Override
|
||||
public void validate(Payload payload) {
|
||||
if (payload == null)
|
||||
throw exception(payload, "Archive must have a payload.");
|
||||
if (payload.getContentMetadata().getContentLength() == null)
|
||||
throw exception(payload, "Content length must be set.");
|
||||
if (payload.getContentMetadata().getContentLength() > MAX_CONTENT_SIZE)
|
||||
throw exception(payload, "Max content size is 4gb" + " but was " + payload.getContentMetadata().getContentLength());
|
||||
}
|
||||
|
||||
protected static IllegalArgumentException exception(Payload payload, String reason) {
|
||||
return new IllegalArgumentException(
|
||||
String.format(
|
||||
"Payload '%s' doesn't match Glacier archive upload rules. "
|
||||
+ "Reason: %s. For more info, please refer to http://docs.aws.amazon.com/amazonglacier/latest/dev/api-archive-post.html.",
|
||||
payload, reason));
|
||||
checkNotNull(payload, "Archive must have a payload.");
|
||||
checkNotNull(payload.getContentMetadata().getContentLength(), "Content length must be set.");
|
||||
checkArgument(payload.getContentMetadata().getContentLength() <= MAX_CONTENT_SIZE,
|
||||
"Max content size is 4gb but was %d", payload.getContentMetadata().getContentLength());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.jclouds.glacier.predicates.validators;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
|
||||
import org.jclouds.predicates.Validator;
|
||||
|
@ -34,26 +35,16 @@ public final class VaultNameValidator extends Validator<String> {
|
|||
private static final int MIN_LENGTH = 1;
|
||||
private static final int MAX_LENGTH = 255;
|
||||
|
||||
private static final CharMatcher VAULT_NAME_ACCEPTABLE_RANGE = CharMatcher.inRange('a', 'z')
|
||||
.or(CharMatcher.inRange('A', 'Z'))
|
||||
.or(CharMatcher.inRange('0', '9'))
|
||||
.or(CharMatcher.anyOf("-_."));
|
||||
|
||||
@Override
|
||||
public void validate(String vaultName) {
|
||||
if (isNullOrEmpty(vaultName) || vaultName.length() > MAX_LENGTH)
|
||||
throw exception(vaultName, "Can't be null or empty. Length must be " + MIN_LENGTH + " to " + MAX_LENGTH
|
||||
+ " symbols.");
|
||||
CharMatcher range = getAcceptableRange();
|
||||
if (!range.matchesAllOf(vaultName))
|
||||
throw exception(vaultName, "Should have ASCII letters and numbers, underscores, hyphens, or periods.");
|
||||
}
|
||||
|
||||
private static CharMatcher getAcceptableRange() {
|
||||
return CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('A', 'Z')).or(CharMatcher.inRange('0', '9'))
|
||||
.or(CharMatcher.anyOf("-_."));
|
||||
}
|
||||
|
||||
protected static IllegalArgumentException exception(String vaultName, String reason) {
|
||||
return new IllegalArgumentException(
|
||||
String.format(
|
||||
"Object '%s' doesn't match AWS Vault naming convention. "
|
||||
+ "Reason: %s. For more info, please refer to http://docs.aws.amazon.com/amazonglacier/latest/dev/api-vault-put.html.",
|
||||
vaultName, reason));
|
||||
checkArgument(!isNullOrEmpty(vaultName) && vaultName.length() <= MAX_LENGTH,
|
||||
"Can't be null or empty. Length must be %d to %d symbols.", MIN_LENGTH, MAX_LENGTH);
|
||||
checkArgument(VAULT_NAME_ACCEPTABLE_RANGE.matchesAllOf(vaultName),
|
||||
"Should contain only ASCII letters and numbers, underscores, hyphens, or periods.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ import com.google.common.io.ByteStreams;
|
|||
import com.google.common.io.Closeables;
|
||||
|
||||
public final class TreeHash {
|
||||
private static final int CHUNK_SIZE = 1024 * 1024;
|
||||
|
||||
private final HashCode treeHash;
|
||||
private final HashCode linearHash;
|
||||
|
||||
|
@ -74,68 +76,64 @@ public final class TreeHash {
|
|||
return "TreeHash [treeHash=" + treeHash + ", linearHash=" + linearHash + "]";
|
||||
}
|
||||
|
||||
public static class Hasher {
|
||||
private static final int CHUNK_SIZE = 1024 * 1024;
|
||||
|
||||
private static HashCode hashList(Collection<HashCode> hashList) {
|
||||
Builder<HashCode> result = ImmutableList.builder();
|
||||
while (hashList.size() > 1) {
|
||||
//Hash pairs of values and add them to the result list.
|
||||
for (Iterator<HashCode> it = hashList.iterator(); it.hasNext();) {
|
||||
HashCode hc1 = it.next();
|
||||
if (it.hasNext()) {
|
||||
HashCode hc2 = it.next();
|
||||
result.add(Hashing.sha256().newHasher()
|
||||
.putBytes(hc1.asBytes())
|
||||
.putBytes(hc2.asBytes())
|
||||
.hash());
|
||||
} else {
|
||||
result.add(hc1);
|
||||
}
|
||||
private static HashCode hashList(Collection<HashCode> hashList) {
|
||||
Builder<HashCode> result = ImmutableList.builder();
|
||||
while (hashList.size() > 1) {
|
||||
//Hash pairs of values and add them to the result list.
|
||||
for (Iterator<HashCode> it = hashList.iterator(); it.hasNext();) {
|
||||
HashCode hc1 = it.next();
|
||||
if (it.hasNext()) {
|
||||
HashCode hc2 = it.next();
|
||||
result.add(Hashing.sha256().newHasher()
|
||||
.putBytes(hc1.asBytes())
|
||||
.putBytes(hc2.asBytes())
|
||||
.hash());
|
||||
} else {
|
||||
result.add(hc1);
|
||||
}
|
||||
hashList = result.build();
|
||||
result = ImmutableList.builder();
|
||||
}
|
||||
return hashList.iterator().next();
|
||||
hashList = result.build();
|
||||
result = ImmutableList.builder();
|
||||
}
|
||||
return hashList.iterator().next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the Hash and the TreeHash values of the payload.
|
||||
*
|
||||
* @return The calculated TreeHash.
|
||||
* @see <a href="http://docs.aws.amazon.com/amazonglacier/latest/dev/checksum-calculations.html" />
|
||||
*/
|
||||
public static TreeHash buildTreeHashFromPayload(Payload payload) throws IOException {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = checkNotNull(payload, "payload").openStream();
|
||||
Builder<HashCode> list = ImmutableList.builder();
|
||||
HashingInputStream linearHis = new HashingInputStream(Hashing.sha256(), is);
|
||||
while (true) {
|
||||
HashingInputStream chunkedHis = new HashingInputStream(
|
||||
Hashing.sha256(), ByteStreams.limit(linearHis, CHUNK_SIZE));
|
||||
long count = ByteStreams.copy(chunkedHis, ByteStreams.nullOutputStream());
|
||||
if (count == 0) {
|
||||
break;
|
||||
}
|
||||
list.add(chunkedHis.hash());
|
||||
}
|
||||
//The result list contains exactly one element now.
|
||||
return new TreeHash(hashList(list.build()), linearHis.hash());
|
||||
} finally {
|
||||
Closeables.closeQuietly(is);
|
||||
/**
|
||||
* Builds the Hash and the TreeHash values of the payload.
|
||||
*
|
||||
* @return The calculated TreeHash.
|
||||
* @see <a href="http://docs.aws.amazon.com/amazonglacier/latest/dev/checksum-calculations.html" />
|
||||
*/
|
||||
public static TreeHash buildTreeHashFromPayload(Payload payload) throws IOException {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = checkNotNull(payload, "payload").openStream();
|
||||
Builder<HashCode> list = ImmutableList.builder();
|
||||
HashingInputStream linearHis = new HashingInputStream(Hashing.sha256(), is);
|
||||
while (true) {
|
||||
HashingInputStream chunkedHis = new HashingInputStream(
|
||||
Hashing.sha256(), ByteStreams.limit(linearHis, CHUNK_SIZE));
|
||||
long count = ByteStreams.copy(chunkedHis, ByteStreams.nullOutputStream());
|
||||
if (count == 0) {
|
||||
break;
|
||||
}
|
||||
list.add(chunkedHis.hash());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a TreeHash based on a map of hashed chunks.
|
||||
*
|
||||
* @return The calculated TreeHash.
|
||||
* @see <a href="http://docs.aws.amazon.com/amazonglacier/latest/dev/checksum-calculations.html" />
|
||||
*/
|
||||
public static HashCode buildTreeHashFromMap(Map<Integer, HashCode> map) {
|
||||
checkArgument(!map.isEmpty(), "The map cannot be empty.");
|
||||
return hashList(ImmutableSortedMap.copyOf(map).values());
|
||||
//The result list contains exactly one element now.
|
||||
return new TreeHash(hashList(list.build()), linearHis.hash());
|
||||
} finally {
|
||||
Closeables.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a TreeHash based on a map of hashed chunks.
|
||||
*
|
||||
* @return The calculated TreeHash.
|
||||
* @see <a href="http://docs.aws.amazon.com/amazonglacier/latest/dev/checksum-calculations.html" />
|
||||
*/
|
||||
public static HashCode buildTreeHashFromMap(Map<Integer, HashCode> map) {
|
||||
checkArgument(!map.isEmpty(), "The map cannot be empty.");
|
||||
return hashList(ImmutableSortedMap.copyOf(map).values());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,14 +31,14 @@ public class PayloadValidatorTest {
|
|||
VALIDATOR.validate(buildPayload(10));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
public void testNoContentLength() {
|
||||
Payload payload = buildPayload(10);
|
||||
payload.getContentMetadata().setContentLength(null);
|
||||
VALIDATOR.validate(payload);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
@Test(expectedExceptions = NullPointerException.class)
|
||||
public void testNullPayload() {
|
||||
VALIDATOR.validate(null);
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ public class TreeHashTest {
|
|||
|
||||
@Test
|
||||
public void testTreeHasherWith1MBPayload() throws IOException {
|
||||
TreeHash th = TreeHash.Hasher.buildTreeHashFromPayload(new ByteSourcePayload(buildData(1 * MiB)));
|
||||
TreeHash th = TreeHash.buildTreeHashFromPayload(new ByteSourcePayload(buildData(1 * MiB)));
|
||||
assertEquals(th.getLinearHash(),
|
||||
HashCode.fromString("9bc1b2a288b26af7257a36277ae3816a7d4f16e89c1e7e77d0a5c48bad62b360"));
|
||||
assertEquals(th.getTreeHash(),
|
||||
|
@ -43,7 +43,7 @@ public class TreeHashTest {
|
|||
|
||||
@Test
|
||||
public void testTreeHasherWith2MBPayload() throws IOException {
|
||||
TreeHash th = TreeHash.Hasher.buildTreeHashFromPayload(new ByteSourcePayload(buildData(2 * MiB)));
|
||||
TreeHash th = TreeHash.buildTreeHashFromPayload(new ByteSourcePayload(buildData(2 * MiB)));
|
||||
assertEquals(th.getLinearHash(),
|
||||
HashCode.fromString("5256ec18f11624025905d057d6befb03d77b243511ac5f77ed5e0221ce6d84b5"));
|
||||
assertEquals(th.getTreeHash(),
|
||||
|
@ -52,7 +52,7 @@ public class TreeHashTest {
|
|||
|
||||
@Test
|
||||
public void testTreeHasherWith3MBPayload() throws IOException {
|
||||
TreeHash th = TreeHash.Hasher.buildTreeHashFromPayload(new ByteSourcePayload(buildData(3 * MiB)));
|
||||
TreeHash th = TreeHash.buildTreeHashFromPayload(new ByteSourcePayload(buildData(3 * MiB)));
|
||||
assertEquals(th.getLinearHash(),
|
||||
HashCode.fromString("6f850bc94ae6f7de14297c01616c36d712d22864497b28a63b81d776b035e656"));
|
||||
assertEquals(th.getTreeHash(),
|
||||
|
@ -61,7 +61,7 @@ public class TreeHashTest {
|
|||
|
||||
@Test
|
||||
public void testTreeHasherWithMoreThan3MBPayload() throws IOException {
|
||||
TreeHash th = TreeHash.Hasher.buildTreeHashFromPayload(new ByteSourcePayload(buildData(3 * MiB + 512 * 1024)));
|
||||
TreeHash th = TreeHash.buildTreeHashFromPayload(new ByteSourcePayload(buildData(3 * MiB + 512 * 1024)));
|
||||
assertEquals(th.getLinearHash(),
|
||||
HashCode.fromString("34c8bdd269f89a091cf17d5d23503940e0abf61c4b6544e42854b9af437f31bb"));
|
||||
assertEquals(th.getTreeHash(),
|
||||
|
@ -73,7 +73,7 @@ public class TreeHashTest {
|
|||
Builder<Integer, HashCode> map = ImmutableMap.<Integer, HashCode>builder();
|
||||
map.put(2, HashCode.fromString("9bc1b2a288b26af7257a36277ae3816a7d4f16e89c1e7e77d0a5c48bad62b360"));
|
||||
map.put(1, HashCode.fromString("9bc1b2a288b26af7257a36277ae3816a7d4f16e89c1e7e77d0a5c48bad62b360"));
|
||||
HashCode treehash = TreeHash.Hasher.buildTreeHashFromMap(map.build());
|
||||
HashCode treehash = TreeHash.buildTreeHashFromMap(map.build());
|
||||
assertEquals(treehash, HashCode.fromString("560c2c9333c719cb00cfdffee3ba293db17f58743cdd1f7e4055373ae6300afa"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue