mirror of https://github.com/apache/jclouds.git
Merge branch 'master' of git@github.com:jclouds/jclouds
* 'master' of git@github.com:jclouds/jclouds: Added container name validator for Azure. Changed the way the validation exceptions are passed through.
This commit is contained in:
commit
19aed31158
|
@ -42,6 +42,7 @@ import org.jclouds.azure.storage.blob.options.CreateContainerOptions;
|
|||
import org.jclouds.azure.storage.blob.options.ListBlobsOptions;
|
||||
import org.jclouds.azure.storage.blob.xml.AccountNameEnumerationResultsHandler;
|
||||
import org.jclouds.azure.storage.blob.xml.ContainerNameEnumerationResultsHandler;
|
||||
import org.jclouds.azure.storage.blob.predicates.validators.ContainerNameValidator;
|
||||
import org.jclouds.azure.storage.domain.BoundedSet;
|
||||
import org.jclouds.azure.storage.filters.SharedKeyLiteAuthentication;
|
||||
import org.jclouds.azure.storage.options.ListOptions;
|
||||
|
@ -56,16 +57,7 @@ import org.jclouds.blobstore.functions.ReturnNullOnKeyNotFound;
|
|||
import org.jclouds.http.functions.ParseETagHeader;
|
||||
import org.jclouds.http.functions.ReturnTrueOn404;
|
||||
import org.jclouds.http.options.GetOptions;
|
||||
import org.jclouds.rest.annotations.BinderParam;
|
||||
import org.jclouds.rest.annotations.Endpoint;
|
||||
import org.jclouds.rest.annotations.ExceptionParser;
|
||||
import org.jclouds.rest.annotations.Headers;
|
||||
import org.jclouds.rest.annotations.ParamParser;
|
||||
import org.jclouds.rest.annotations.QueryParams;
|
||||
import org.jclouds.rest.annotations.RequestFilters;
|
||||
import org.jclouds.rest.annotations.ResponseParser;
|
||||
import org.jclouds.rest.annotations.SkipEncoding;
|
||||
import org.jclouds.rest.annotations.XMLResponseParser;
|
||||
import org.jclouds.rest.annotations.*;
|
||||
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
@ -107,7 +99,8 @@ public interface AzureBlobAsyncClient {
|
|||
@Path("{container}")
|
||||
@ExceptionParser(ReturnFalseIfContainerAlreadyExists.class)
|
||||
@QueryParams(keys = "restype", values = "container")
|
||||
ListenableFuture<Boolean> createContainer(@PathParam("container") String container,
|
||||
ListenableFuture<Boolean> createContainer(
|
||||
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container,
|
||||
CreateContainerOptions... options);
|
||||
|
||||
/**
|
||||
|
@ -119,7 +112,7 @@ public interface AzureBlobAsyncClient {
|
|||
@ResponseParser(ParseContainerPropertiesFromHeaders.class)
|
||||
@ExceptionParser(ReturnNullOnContainerNotFound.class)
|
||||
ListenableFuture<ContainerProperties> getContainerProperties(
|
||||
@PathParam("container") String container);
|
||||
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container);
|
||||
|
||||
/**
|
||||
* @see AzureBlobClient#containerExists
|
||||
|
@ -128,7 +121,8 @@ public interface AzureBlobAsyncClient {
|
|||
@Path("{container}")
|
||||
@QueryParams(keys = "restype", values = "container")
|
||||
@ExceptionParser(ReturnFalseOnContainerNotFound.class)
|
||||
ListenableFuture<Boolean> containerExists(@PathParam("container") String container);
|
||||
ListenableFuture<Boolean> containerExists(
|
||||
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container);
|
||||
|
||||
/**
|
||||
* @see AzureBlobClient#setResourceMetadata
|
||||
|
@ -136,7 +130,8 @@ public interface AzureBlobAsyncClient {
|
|||
@PUT
|
||||
@Path("{container}")
|
||||
@QueryParams(keys = { "restype", "comp" }, values = { "container", "metadata" })
|
||||
ListenableFuture<Void> setResourceMetadata(@PathParam("container") String container,
|
||||
ListenableFuture<Void> setResourceMetadata(
|
||||
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container,
|
||||
@BinderParam(BindMapToHeadersWithPrefix.class) Map<String, String> metadata);
|
||||
|
||||
/**
|
||||
|
@ -146,7 +141,8 @@ public interface AzureBlobAsyncClient {
|
|||
@Path("{container}")
|
||||
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
||||
@QueryParams(keys = "restype", values = "container")
|
||||
ListenableFuture<Void> deleteContainer(@PathParam("container") String container);
|
||||
ListenableFuture<Void> deleteContainer(
|
||||
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container);
|
||||
|
||||
/**
|
||||
* @see AzureBlobClient#createRootContainer
|
||||
|
@ -167,17 +163,18 @@ public interface AzureBlobAsyncClient {
|
|||
ListenableFuture<Void> deleteRootContainer();
|
||||
|
||||
/**
|
||||
* @see AzureBlobClient#listBlobs(String, ListBlobsOptions)
|
||||
* @see AzureBlobClient#listBlobs(String, ListBlobsOptions[])
|
||||
*/
|
||||
@GET
|
||||
@XMLResponseParser(ContainerNameEnumerationResultsHandler.class)
|
||||
@Path("{container}")
|
||||
@QueryParams(keys = { "restype", "comp" }, values = { "container", "list" })
|
||||
ListenableFuture<ListBlobsResponse> listBlobs(@PathParam("container") String container,
|
||||
ListenableFuture<ListBlobsResponse> listBlobs(
|
||||
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container,
|
||||
ListBlobsOptions... options);
|
||||
|
||||
/**
|
||||
* @see AzureBlobClient#listBlobs(ListBlobsOptions)
|
||||
* @see AzureBlobClient#listBlobs(ListBlobsOptions[])
|
||||
*/
|
||||
@GET
|
||||
@XMLResponseParser(ContainerNameEnumerationResultsHandler.class)
|
||||
|
@ -192,7 +189,7 @@ public interface AzureBlobAsyncClient {
|
|||
@Path("{container}/{name}")
|
||||
@ResponseParser(ParseETagHeader.class)
|
||||
ListenableFuture<String> putBlob(
|
||||
@PathParam("container") String container,
|
||||
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container,
|
||||
@PathParam("name") @ParamParser(BlobName.class) @BinderParam(BindAzureBlobToPayload.class) org.jclouds.azure.storage.blob.domain.AzureBlob object);
|
||||
|
||||
/**
|
||||
|
@ -203,7 +200,8 @@ public interface AzureBlobAsyncClient {
|
|||
@ExceptionParser(ReturnNullOnKeyNotFound.class)
|
||||
@Path("{container}/{name}")
|
||||
ListenableFuture<org.jclouds.azure.storage.blob.domain.AzureBlob> getBlob(
|
||||
@PathParam("container") String container, @PathParam("name") String name,
|
||||
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container,
|
||||
@PathParam("name") String name,
|
||||
GetOptions... options);
|
||||
|
||||
/**
|
||||
|
@ -213,7 +211,8 @@ public interface AzureBlobAsyncClient {
|
|||
@ResponseParser(ParseBlobPropertiesFromHeaders.class)
|
||||
@ExceptionParser(ReturnNullOnKeyNotFound.class)
|
||||
@Path("{container}/{name}")
|
||||
ListenableFuture<BlobProperties> getBlobProperties(@PathParam("container") String container,
|
||||
ListenableFuture<BlobProperties> getBlobProperties(
|
||||
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container,
|
||||
@PathParam("name") String name);
|
||||
|
||||
/**
|
||||
|
@ -222,7 +221,8 @@ public interface AzureBlobAsyncClient {
|
|||
@HEAD
|
||||
@ExceptionParser(ReturnFalseOnKeyNotFound.class)
|
||||
@Path("{container}/{name}")
|
||||
ListenableFuture<Boolean> blobExists(@PathParam("container") String container,
|
||||
ListenableFuture<Boolean> blobExists(
|
||||
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container,
|
||||
@PathParam("name") String name);
|
||||
|
||||
/**
|
||||
|
@ -231,7 +231,8 @@ public interface AzureBlobAsyncClient {
|
|||
@PUT
|
||||
@Path("{container}/{name}")
|
||||
@QueryParams(keys = { "comp" }, values = { "metadata" })
|
||||
ListenableFuture<Void> setBlobMetadata(@PathParam("container") String container,
|
||||
ListenableFuture<Void> setBlobMetadata(
|
||||
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container,
|
||||
@PathParam("name") String name,
|
||||
@BinderParam(BindMapToHeadersWithPrefix.class) Map<String, String> metadata);
|
||||
|
||||
|
@ -241,7 +242,8 @@ public interface AzureBlobAsyncClient {
|
|||
@DELETE
|
||||
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
||||
@Path("{container}/{name}")
|
||||
ListenableFuture<Void> deleteBlob(@PathParam("container") String container,
|
||||
ListenableFuture<Void> deleteBlob(
|
||||
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container,
|
||||
@PathParam("name") String name);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ====================================================================
|
||||
*/
|
||||
|
||||
package org.jclouds.azure.storage.blob.predicates.validators;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
import org.jclouds.predicates.Validator;
|
||||
import javax.annotation.Nullable;
|
||||
import static com.google.common.base.CharMatcher.*;
|
||||
|
||||
/**
|
||||
* Validates name for Azure container.
|
||||
* The complete requirements are listed at:
|
||||
* http://weblogs.asp.net/vblasberg/archive/2009/02/17/azure-details-and-limitations-blobs-tables-and-queues.aspx
|
||||
*
|
||||
* @see org.jclouds.rest.InputParamValidator
|
||||
* @see org.jclouds.predicates.Validator
|
||||
*
|
||||
* @author Oleksiy Yarmula
|
||||
*/
|
||||
public class ContainerNameValidator extends Validator<String> {
|
||||
|
||||
public void validate(@Nullable String containerName) {
|
||||
|
||||
if(containerName == null || containerName.length() < 3 || containerName.length() > 63) throw exception(containerName, "Can't be null or empty. Length must be 3 to 63 symbols.");
|
||||
if(CharMatcher.JAVA_LETTER_OR_DIGIT.indexIn(containerName) != 0) throw exception(containerName, "Should start with letter/number");
|
||||
if(!containerName.toLowerCase().equals(containerName)) throw exception(containerName, "Should be only lowercase");
|
||||
|
||||
/* The name must be a valid DNS name. From wikipedia:
|
||||
"The characters allowed in a label are a subset of the ASCII character set, a
|
||||
and includes the characters a through z, A through Z, digits 0 through 9".
|
||||
From Azure:
|
||||
Every Dash (-) Must Be Immediately Preceded and Followed by a Letter or Number.
|
||||
*/
|
||||
CharMatcher lettersNumbersOrDashes = inRange('a', 'z').or(inRange('0', '9').or(is('-')));
|
||||
if(! lettersNumbersOrDashes.matchesAllOf(containerName)) throw exception(containerName, "Should have lowercase ASCII letters, " +
|
||||
"numbers, or dashes");
|
||||
if(containerName.contains("--")) throw exception(containerName, "Every dash must be followed by letter or number");
|
||||
if(containerName.endsWith("-")) throw exception(containerName, "Shouldn't end with a dash");
|
||||
}
|
||||
|
||||
private IllegalArgumentException exception(String containerName, String reason) {
|
||||
return new IllegalArgumentException(String.format("Object '%s' doesn't match Azure container naming convention. " +
|
||||
"Reason: %s. For more info, please refer to http://weblogs.asp.net/vblasberg/archive/2009/02/17/" +
|
||||
"azure-details-and-limitations-blobs-tables-and-queues.aspx.", containerName, reason));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ====================================================================
|
||||
*/
|
||||
|
||||
package org.jclouds.azure.storage.predicates.validators;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import org.jclouds.azure.storage.blob.predicates.validators.ContainerNameValidator;
|
||||
|
||||
public class ContainerNameValidatorTest {
|
||||
|
||||
@Test
|
||||
public void testNamesValidity() {
|
||||
ContainerNameValidator validator = new ContainerNameValidator();
|
||||
|
||||
validator.validate("adasd");
|
||||
|
||||
validator.validate("adasd-ab");
|
||||
|
||||
validator.validate("zzz");
|
||||
validator.validate("abcefghjlkmnop");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidNames() {
|
||||
|
||||
ContainerNameValidator validator = new ContainerNameValidator();
|
||||
|
||||
try {
|
||||
//double dash - should fail
|
||||
validator.validate("adasd-ab--baba");
|
||||
throw new RuntimeException("to be converted to TestException later");
|
||||
} catch(IllegalArgumentException e) {
|
||||
// supposed to happen - continue
|
||||
}
|
||||
|
||||
try {
|
||||
// dots - should fail
|
||||
validator.validate("abc.zz.la");
|
||||
throw new RuntimeException("to be converted to TestException later");
|
||||
} catch(IllegalArgumentException e) {
|
||||
// supposed to happen - continue
|
||||
}
|
||||
|
||||
try {
|
||||
// uppercase - should fail
|
||||
validator.validate("abcZZla");
|
||||
throw new RuntimeException("to be converted to TestException later");
|
||||
} catch(IllegalArgumentException e) {
|
||||
// supposed to happen - continue
|
||||
}
|
||||
|
||||
try {
|
||||
validator.validate("zz");
|
||||
throw new RuntimeException("to be converted to TestException later");
|
||||
} catch(IllegalArgumentException e) {
|
||||
// supposed to happen - continue
|
||||
}
|
||||
|
||||
try {
|
||||
// non-ASCII - should fail
|
||||
validator.validate("a????");
|
||||
throw new RuntimeException("to be converted to TestException later");
|
||||
} catch(IllegalArgumentException e) {
|
||||
// supposed to happen - continue
|
||||
}
|
||||
|
||||
try {
|
||||
// starts with dash - should fail
|
||||
validator.validate("-adasd");
|
||||
throw new RuntimeException("to be converted to TestException later");
|
||||
} catch(IllegalArgumentException e) {
|
||||
// supposed to happen - continue
|
||||
}
|
||||
|
||||
try {
|
||||
// ends with dash - should fail
|
||||
validator.validate("adasd-");
|
||||
throw new RuntimeException("to be converted to TestException later");
|
||||
} catch(IllegalArgumentException e) {
|
||||
// supposed to happen - continue
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,3 +1,22 @@
|
|||
/**
|
||||
*
|
||||
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ====================================================================
|
||||
*/
|
||||
|
||||
package org.jclouds.predicates;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
@ -20,12 +39,8 @@ public abstract class Validator<T> implements Predicate<T> {
|
|||
|
||||
@Override
|
||||
public boolean apply(@Nullable T t) {
|
||||
try {
|
||||
validate(t);
|
||||
return true; // by contract
|
||||
} catch(IllegalArgumentException iae) {
|
||||
return false; // by contract
|
||||
}
|
||||
return true; // by contract, if no exception thrown
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -71,56 +71,54 @@ public class InputParamValidator {
|
|||
*/
|
||||
public void validateMethodParametersOrThrow(Method method, Object... args) {
|
||||
|
||||
if (!passesMethodValidation(method, args)
|
||||
|| !passesParameterValidation(method.getParameterAnnotations(), args)) {
|
||||
|
||||
try {
|
||||
performMethodValidation(method, args);
|
||||
performParameterValidation(method.getParameterAnnotations(), args);
|
||||
} catch(IllegalArgumentException e) {
|
||||
String argsString = Iterables.toString(Arrays.asList(args));
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Validation on '%s#%s' didn't pass for arguments: " + "%s", method
|
||||
.getDeclaringClass().getName(), method.getName(), argsString));
|
||||
throw new IllegalArgumentException(String.format("Validation on '%s#%s' didn't pass for arguments: " +
|
||||
"%s. %n Reason: %s.", method.getDeclaringClass().getName(), method.getName(), argsString,
|
||||
e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if all the method parameters passed all of the method-level validators.
|
||||
*
|
||||
* Returns if all the method parameters passed all of the method-level
|
||||
* validators or throws an {@link IllegalArgumentException}.
|
||||
* @param method
|
||||
* method with optionally set {@link ParamValidators}. This can not be null.
|
||||
* @param args
|
||||
* method's parameters
|
||||
* @return true if all the method's parameters pass all method-level validators
|
||||
*/
|
||||
private boolean passesMethodValidation(Method method, Object... args) {
|
||||
private void performMethodValidation(Method method, Object... args) {
|
||||
ParamValidators paramValidatorsAnnotation = checkNotNull(method).getAnnotation(
|
||||
ParamValidators.class);
|
||||
if (paramValidatorsAnnotation == null)
|
||||
return true; // by contract
|
||||
return; // by contract
|
||||
|
||||
List<Validator<?>> methodValidators = getValidatorsFromAnnotation(paramValidatorsAnnotation);
|
||||
|
||||
return runPredicatesAgainstArgs(methodValidators, args);
|
||||
runPredicatesAgainstArgs(methodValidators, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if all the method parameters passed all of their corresponding validators.
|
||||
* Returns if all the method parameters passed all of their corresponding
|
||||
* validators or throws an {@link IllegalArgumentException}.
|
||||
*
|
||||
* @param annotations
|
||||
* annotations for method's arguments
|
||||
* @param args
|
||||
* arguments that correspond to the array of annotations
|
||||
* @return true if all the method parameters passed all of their corresponding validators.
|
||||
*/
|
||||
private boolean passesParameterValidation(Annotation[][] annotations, Object... args) {
|
||||
boolean allPreducatesTrue = true;
|
||||
private void performParameterValidation(Annotation[][] annotations, Object... args) {
|
||||
for (int currentParameterIndex = 0; currentParameterIndex < annotations.length; currentParameterIndex++) {
|
||||
ParamValidators annotation = findParamValidatorsAnnotationOrReturnNull(annotations[currentParameterIndex]);
|
||||
if (annotation == null)
|
||||
continue;
|
||||
List<Validator<?>> parameterValidators = getValidatorsFromAnnotation(annotation);
|
||||
allPreducatesTrue &= runPredicatesAgainstArgs(parameterValidators,
|
||||
runPredicatesAgainstArgs(parameterValidators,
|
||||
args[currentParameterIndex]);
|
||||
}
|
||||
return allPreducatesTrue;
|
||||
}
|
||||
|
||||
private List<Validator<?>> getValidatorsFromAnnotation(ParamValidators paramValidatorsAnnotation) {
|
||||
|
@ -132,12 +130,10 @@ public class InputParamValidator {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean runPredicatesAgainstArgs(List<Validator<?>> predicates, Object... args) {
|
||||
boolean allPredicatesTrue = true;
|
||||
private void runPredicatesAgainstArgs(List<Validator<?>> predicates, Object... args) {
|
||||
for (Validator validator : predicates) {
|
||||
allPredicatesTrue &= Iterables.all(Arrays.asList(args), validator);
|
||||
Iterables.all(Arrays.asList(args), validator);
|
||||
}
|
||||
return allPredicatesTrue;
|
||||
}
|
||||
|
||||
private ParamValidators findParamValidatorsAnnotationOrReturnNull(
|
||||
|
|
Loading…
Reference in New Issue