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.options.ListBlobsOptions;
|
||||||
import org.jclouds.azure.storage.blob.xml.AccountNameEnumerationResultsHandler;
|
import org.jclouds.azure.storage.blob.xml.AccountNameEnumerationResultsHandler;
|
||||||
import org.jclouds.azure.storage.blob.xml.ContainerNameEnumerationResultsHandler;
|
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.domain.BoundedSet;
|
||||||
import org.jclouds.azure.storage.filters.SharedKeyLiteAuthentication;
|
import org.jclouds.azure.storage.filters.SharedKeyLiteAuthentication;
|
||||||
import org.jclouds.azure.storage.options.ListOptions;
|
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.ParseETagHeader;
|
||||||
import org.jclouds.http.functions.ReturnTrueOn404;
|
import org.jclouds.http.functions.ReturnTrueOn404;
|
||||||
import org.jclouds.http.options.GetOptions;
|
import org.jclouds.http.options.GetOptions;
|
||||||
import org.jclouds.rest.annotations.BinderParam;
|
import org.jclouds.rest.annotations.*;
|
||||||
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.functions.ReturnVoidOnNotFoundOr404;
|
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
@ -107,7 +99,8 @@ public interface AzureBlobAsyncClient {
|
||||||
@Path("{container}")
|
@Path("{container}")
|
||||||
@ExceptionParser(ReturnFalseIfContainerAlreadyExists.class)
|
@ExceptionParser(ReturnFalseIfContainerAlreadyExists.class)
|
||||||
@QueryParams(keys = "restype", values = "container")
|
@QueryParams(keys = "restype", values = "container")
|
||||||
ListenableFuture<Boolean> createContainer(@PathParam("container") String container,
|
ListenableFuture<Boolean> createContainer(
|
||||||
|
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container,
|
||||||
CreateContainerOptions... options);
|
CreateContainerOptions... options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -119,7 +112,7 @@ public interface AzureBlobAsyncClient {
|
||||||
@ResponseParser(ParseContainerPropertiesFromHeaders.class)
|
@ResponseParser(ParseContainerPropertiesFromHeaders.class)
|
||||||
@ExceptionParser(ReturnNullOnContainerNotFound.class)
|
@ExceptionParser(ReturnNullOnContainerNotFound.class)
|
||||||
ListenableFuture<ContainerProperties> getContainerProperties(
|
ListenableFuture<ContainerProperties> getContainerProperties(
|
||||||
@PathParam("container") String container);
|
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see AzureBlobClient#containerExists
|
* @see AzureBlobClient#containerExists
|
||||||
|
@ -128,7 +121,8 @@ public interface AzureBlobAsyncClient {
|
||||||
@Path("{container}")
|
@Path("{container}")
|
||||||
@QueryParams(keys = "restype", values = "container")
|
@QueryParams(keys = "restype", values = "container")
|
||||||
@ExceptionParser(ReturnFalseOnContainerNotFound.class)
|
@ExceptionParser(ReturnFalseOnContainerNotFound.class)
|
||||||
ListenableFuture<Boolean> containerExists(@PathParam("container") String container);
|
ListenableFuture<Boolean> containerExists(
|
||||||
|
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see AzureBlobClient#setResourceMetadata
|
* @see AzureBlobClient#setResourceMetadata
|
||||||
|
@ -136,7 +130,8 @@ public interface AzureBlobAsyncClient {
|
||||||
@PUT
|
@PUT
|
||||||
@Path("{container}")
|
@Path("{container}")
|
||||||
@QueryParams(keys = { "restype", "comp" }, values = { "container", "metadata" })
|
@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);
|
@BinderParam(BindMapToHeadersWithPrefix.class) Map<String, String> metadata);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -146,7 +141,8 @@ public interface AzureBlobAsyncClient {
|
||||||
@Path("{container}")
|
@Path("{container}")
|
||||||
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
||||||
@QueryParams(keys = "restype", values = "container")
|
@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
|
* @see AzureBlobClient#createRootContainer
|
||||||
|
@ -167,17 +163,18 @@ public interface AzureBlobAsyncClient {
|
||||||
ListenableFuture<Void> deleteRootContainer();
|
ListenableFuture<Void> deleteRootContainer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see AzureBlobClient#listBlobs(String, ListBlobsOptions)
|
* @see AzureBlobClient#listBlobs(String, ListBlobsOptions[])
|
||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@XMLResponseParser(ContainerNameEnumerationResultsHandler.class)
|
@XMLResponseParser(ContainerNameEnumerationResultsHandler.class)
|
||||||
@Path("{container}")
|
@Path("{container}")
|
||||||
@QueryParams(keys = { "restype", "comp" }, values = { "container", "list" })
|
@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);
|
ListBlobsOptions... options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see AzureBlobClient#listBlobs(ListBlobsOptions)
|
* @see AzureBlobClient#listBlobs(ListBlobsOptions[])
|
||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@XMLResponseParser(ContainerNameEnumerationResultsHandler.class)
|
@XMLResponseParser(ContainerNameEnumerationResultsHandler.class)
|
||||||
|
@ -192,7 +189,7 @@ public interface AzureBlobAsyncClient {
|
||||||
@Path("{container}/{name}")
|
@Path("{container}/{name}")
|
||||||
@ResponseParser(ParseETagHeader.class)
|
@ResponseParser(ParseETagHeader.class)
|
||||||
ListenableFuture<String> putBlob(
|
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);
|
@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)
|
@ExceptionParser(ReturnNullOnKeyNotFound.class)
|
||||||
@Path("{container}/{name}")
|
@Path("{container}/{name}")
|
||||||
ListenableFuture<org.jclouds.azure.storage.blob.domain.AzureBlob> getBlob(
|
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);
|
GetOptions... options);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -213,7 +211,8 @@ public interface AzureBlobAsyncClient {
|
||||||
@ResponseParser(ParseBlobPropertiesFromHeaders.class)
|
@ResponseParser(ParseBlobPropertiesFromHeaders.class)
|
||||||
@ExceptionParser(ReturnNullOnKeyNotFound.class)
|
@ExceptionParser(ReturnNullOnKeyNotFound.class)
|
||||||
@Path("{container}/{name}")
|
@Path("{container}/{name}")
|
||||||
ListenableFuture<BlobProperties> getBlobProperties(@PathParam("container") String container,
|
ListenableFuture<BlobProperties> getBlobProperties(
|
||||||
|
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container,
|
||||||
@PathParam("name") String name);
|
@PathParam("name") String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -222,7 +221,8 @@ public interface AzureBlobAsyncClient {
|
||||||
@HEAD
|
@HEAD
|
||||||
@ExceptionParser(ReturnFalseOnKeyNotFound.class)
|
@ExceptionParser(ReturnFalseOnKeyNotFound.class)
|
||||||
@Path("{container}/{name}")
|
@Path("{container}/{name}")
|
||||||
ListenableFuture<Boolean> blobExists(@PathParam("container") String container,
|
ListenableFuture<Boolean> blobExists(
|
||||||
|
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container,
|
||||||
@PathParam("name") String name);
|
@PathParam("name") String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -231,7 +231,8 @@ public interface AzureBlobAsyncClient {
|
||||||
@PUT
|
@PUT
|
||||||
@Path("{container}/{name}")
|
@Path("{container}/{name}")
|
||||||
@QueryParams(keys = { "comp" }, values = { "metadata" })
|
@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,
|
@PathParam("name") String name,
|
||||||
@BinderParam(BindMapToHeadersWithPrefix.class) Map<String, String> metadata);
|
@BinderParam(BindMapToHeadersWithPrefix.class) Map<String, String> metadata);
|
||||||
|
|
||||||
|
@ -241,7 +242,8 @@ public interface AzureBlobAsyncClient {
|
||||||
@DELETE
|
@DELETE
|
||||||
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
|
||||||
@Path("{container}/{name}")
|
@Path("{container}/{name}")
|
||||||
ListenableFuture<Void> deleteBlob(@PathParam("container") String container,
|
ListenableFuture<Void> deleteBlob(
|
||||||
|
@PathParam("container") @ParamValidators({ContainerNameValidator.class}) String container,
|
||||||
@PathParam("name") String name);
|
@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;
|
package org.jclouds.predicates;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
@ -20,12 +39,8 @@ public abstract class Validator<T> implements Predicate<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(@Nullable T t) {
|
public boolean apply(@Nullable T t) {
|
||||||
try {
|
validate(t);
|
||||||
validate(t);
|
return true; // by contract, if no exception thrown
|
||||||
return true; // by contract
|
|
||||||
} catch(IllegalArgumentException iae) {
|
|
||||||
return false; // by contract
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -36,117 +36,113 @@ import com.google.inject.Injector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates method parameters.
|
* Validates method parameters.
|
||||||
*
|
*
|
||||||
* Checks the {@link ParamValidators} annotation for validators. There can be method-level
|
* Checks the {@link ParamValidators} annotation for validators. There can be method-level
|
||||||
* validators that apply to all parameters, and parameter-level validators. When validation on at
|
* validators that apply to all parameters, and parameter-level validators. When validation on at
|
||||||
* least one parameter doesn't pass, throws {@link IllegalStateException}.
|
* least one parameter doesn't pass, throws {@link IllegalStateException}.
|
||||||
*
|
*
|
||||||
* @author Oleksiy Yarmula
|
* @author Oleksiy Yarmula
|
||||||
*/
|
*/
|
||||||
public class InputParamValidator {
|
public class InputParamValidator {
|
||||||
|
|
||||||
private final Injector injector;
|
private final Injector injector;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public InputParamValidator(Injector injector) {
|
public InputParamValidator(Injector injector) {
|
||||||
this.injector = injector;
|
this.injector = injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputParamValidator() {
|
public InputParamValidator() {
|
||||||
injector = null;
|
injector = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates that method parameters are correct, according to {@link ParamValidators}.
|
* Validates that method parameters are correct, according to {@link ParamValidators}.
|
||||||
*
|
*
|
||||||
* @param method
|
* @param method
|
||||||
* method with optionally set {@link ParamValidators}
|
* method with optionally set {@link ParamValidators}
|
||||||
* @param args
|
* @param args
|
||||||
* method arguments with optionally set {@link ParamValidators}
|
* method arguments with optionally set {@link ParamValidators}
|
||||||
* @see ParamValidators
|
* @see ParamValidators
|
||||||
* @see Validator
|
* @see Validator
|
||||||
*
|
*
|
||||||
* @throws IllegalStateException
|
* @throws IllegalStateException
|
||||||
* if validation failed
|
* if validation failed
|
||||||
*/
|
*/
|
||||||
public void validateMethodParametersOrThrow(Method method, Object... args) {
|
public void validateMethodParametersOrThrow(Method method, Object... args) {
|
||||||
|
|
||||||
if (!passesMethodValidation(method, args)
|
try {
|
||||||
|| !passesParameterValidation(method.getParameterAnnotations(), args)) {
|
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. %n Reason: %s.", method.getDeclaringClass().getName(), method.getName(), argsString,
|
||||||
|
e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String argsString = Iterables.toString(Arrays.asList(args));
|
/**
|
||||||
throw new IllegalArgumentException(String.format(
|
* Returns if all the method parameters passed all of the method-level
|
||||||
"Validation on '%s#%s' didn't pass for arguments: " + "%s", method
|
* validators or throws an {@link IllegalArgumentException}.
|
||||||
.getDeclaringClass().getName(), method.getName(), argsString));
|
* @param method
|
||||||
}
|
* method with optionally set {@link ParamValidators}. This can not be null.
|
||||||
}
|
* @param args
|
||||||
|
* method's parameters
|
||||||
|
*/
|
||||||
|
private void performMethodValidation(Method method, Object... args) {
|
||||||
|
ParamValidators paramValidatorsAnnotation = checkNotNull(method).getAnnotation(
|
||||||
|
ParamValidators.class);
|
||||||
|
if (paramValidatorsAnnotation == null)
|
||||||
|
return; // by contract
|
||||||
|
|
||||||
/**
|
List<Validator<?>> methodValidators = getValidatorsFromAnnotation(paramValidatorsAnnotation);
|
||||||
* Returns true if all the method parameters passed all of the method-level validators.
|
|
||||||
*
|
|
||||||
* @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) {
|
|
||||||
ParamValidators paramValidatorsAnnotation = checkNotNull(method).getAnnotation(
|
|
||||||
ParamValidators.class);
|
|
||||||
if (paramValidatorsAnnotation == null)
|
|
||||||
return true; // by contract
|
|
||||||
|
|
||||||
List<Validator<?>> methodValidators = getValidatorsFromAnnotation(paramValidatorsAnnotation);
|
runPredicatesAgainstArgs(methodValidators, args);
|
||||||
|
}
|
||||||
|
|
||||||
return runPredicatesAgainstArgs(methodValidators, args);
|
/**
|
||||||
}
|
* 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
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
runPredicatesAgainstArgs(parameterValidators,
|
||||||
|
args[currentParameterIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
private List<Validator<?>> getValidatorsFromAnnotation(ParamValidators paramValidatorsAnnotation) {
|
||||||
* Returns true if all the method parameters passed all of their corresponding validators.
|
List<Validator<?>> validators = Lists.newArrayList();
|
||||||
*
|
for (Class<? extends Validator<?>> validator : paramValidatorsAnnotation.value()) {
|
||||||
* @param annotations
|
validators.add(checkNotNull(injector.getInstance(validator)));
|
||||||
* annotations for method's arguments
|
}
|
||||||
* @param args
|
return validators;
|
||||||
* 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;
|
|
||||||
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,
|
|
||||||
args[currentParameterIndex]);
|
|
||||||
}
|
|
||||||
return allPreducatesTrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Validator<?>> getValidatorsFromAnnotation(ParamValidators paramValidatorsAnnotation) {
|
@SuppressWarnings("unchecked")
|
||||||
List<Validator<?>> validators = Lists.newArrayList();
|
private void runPredicatesAgainstArgs(List<Validator<?>> predicates, Object... args) {
|
||||||
for (Class<? extends Validator<?>> validator : paramValidatorsAnnotation.value()) {
|
for (Validator validator : predicates) {
|
||||||
validators.add(checkNotNull(injector.getInstance(validator)));
|
Iterables.all(Arrays.asList(args), validator);
|
||||||
}
|
}
|
||||||
return validators;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
private ParamValidators findParamValidatorsAnnotationOrReturnNull(
|
||||||
private boolean runPredicatesAgainstArgs(List<Validator<?>> predicates, Object... args) {
|
|
||||||
boolean allPredicatesTrue = true;
|
|
||||||
for (Validator validator : predicates) {
|
|
||||||
allPredicatesTrue &= Iterables.all(Arrays.asList(args), validator);
|
|
||||||
}
|
|
||||||
return allPredicatesTrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ParamValidators findParamValidatorsAnnotationOrReturnNull(
|
|
||||||
Annotation[] parameterAnnotations) {
|
Annotation[] parameterAnnotations) {
|
||||||
for (Annotation annotation : parameterAnnotations) {
|
for (Annotation annotation : parameterAnnotations) {
|
||||||
if (annotation instanceof ParamValidators)
|
if (annotation instanceof ParamValidators)
|
||||||
return (ParamValidators) annotation;
|
return (ParamValidators) annotation;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue