From 2d65a4bf0e6c113326c9d62bef42dec265b01cff Mon Sep 17 00:00:00 2001 From: Christian Kaltepoth Date: Mon, 3 Sep 2012 07:25:50 +0200 Subject: [PATCH] Added support for @Validate to annotations module (#65) --- .../ocpsoft/rewrite/annotation/Validate.java | 32 +++++ .../annotation/handler/ValidateHandler.java | 117 ++++++++++++++++++ .../spi/DefaultValidatorProvider.java | 51 ++++++++ .../annotation/spi/ValidatorProvider.java | 35 ++++++ ...t.rewrite.annotation.spi.AnnotationHandler | 3 +- ...t.rewrite.annotation.spi.ValidatorProvider | 1 + .../validate/CustomValidatorBean.java | 30 +++++ .../validate/CustomValidatorTest.java | 49 ++++++++ .../validate/EvenLengthValidator.java | 16 +++ 9 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/Validate.java create mode 100644 config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/handler/ValidateHandler.java create mode 100644 config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/spi/DefaultValidatorProvider.java create mode 100644 config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/spi/ValidatorProvider.java create mode 100644 config-annotations/src/main/resources/META-INF/services/org.ocpsoft.rewrite.annotation.spi.ValidatorProvider create mode 100644 config-annotations/src/test/java/org/ocpsoft/rewrite/annotation/validate/CustomValidatorBean.java create mode 100644 config-annotations/src/test/java/org/ocpsoft/rewrite/annotation/validate/CustomValidatorTest.java create mode 100644 config-annotations/src/test/java/org/ocpsoft/rewrite/annotation/validate/EvenLengthValidator.java diff --git a/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/Validate.java b/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/Validate.java new file mode 100644 index 000000000..cb5ff7f54 --- /dev/null +++ b/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/Validate.java @@ -0,0 +1,32 @@ +/* + * Copyright 2011 Lincoln Baxter, III + * + * 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.ocpsoft.rewrite.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Inherited +@Documented +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Validate +{ + Class with(); +} diff --git a/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/handler/ValidateHandler.java b/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/handler/ValidateHandler.java new file mode 100644 index 000000000..637a99417 --- /dev/null +++ b/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/handler/ValidateHandler.java @@ -0,0 +1,117 @@ +/* + * Copyright 2011 Lincoln Baxter, III + * + * 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.ocpsoft.rewrite.annotation.handler; + +import java.lang.reflect.Field; +import java.util.Iterator; + +import org.ocpsoft.common.services.ServiceLoader; +import org.ocpsoft.common.util.Assert; +import org.ocpsoft.logging.Logger; +import org.ocpsoft.rewrite.annotation.Validate; +import org.ocpsoft.rewrite.annotation.api.FieldContext; +import org.ocpsoft.rewrite.annotation.api.HandlerChain; +import org.ocpsoft.rewrite.annotation.spi.FieldAnnotationHandler; +import org.ocpsoft.rewrite.annotation.spi.ValidatorProvider; +import org.ocpsoft.rewrite.bind.BindingBuilder; +import org.ocpsoft.rewrite.bind.Validator; +import org.ocpsoft.rewrite.context.EvaluationContext; +import org.ocpsoft.rewrite.event.Rewrite; + +/** + * Handler for {@link Validate}. + * + * @author Christian Kaltepoth + */ +public class ValidateHandler extends FieldAnnotationHandler +{ + private final Logger log = Logger.getLogger(ValidateHandler.class); + + @Override + public Class handles() + { + return Validate.class; + } + + @Override + public int priority() + { + return HandlerWeights.WEIGHT_TYPE_ENRICHING; + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void process(FieldContext context, Validate annotation, HandlerChain chain) + { + Field field = context.getJavaField(); + + // locate the binding previously created by @ParameterBinding + BindingBuilder bindingBuilder = (BindingBuilder) context.get(BindingBuilder.class); + Assert.notNull(bindingBuilder, "No binding found for field: " + field); + + // add the validator + Class validatorType = annotation.with(); + LazyValidatorAdapter validator = new LazyValidatorAdapter(validatorType); + bindingBuilder.validatedBy(validator); + + // some logging + if (log.isTraceEnabled()) { + log.trace("Attached validator adapeter for [{}] to field [{}] of class [{}]", new Object[] { + validatorType.getSimpleName(), field.getName(), field.getDeclaringClass().getName() + }); + } + + // continue with the chain + chain.proceed(); + + } + + /** + * This class uses the {@link ValidatorProvider} SPI to lazily obtain the {@link Validator} for a given {@link Class} + * instance. + */ + private static class LazyValidatorAdapter implements Validator + { + + private final Class validatorClass; + + public LazyValidatorAdapter(Class clazz) + { + this.validatorClass = clazz; + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public boolean validate(Rewrite event, EvaluationContext context, Object value) + { + + Validator validator = null; + + // let one of the SPI implementations build the validator + Iterator providers = ServiceLoader.load(ValidatorProvider.class).iterator(); + while (providers.hasNext()) { + validator = providers.next().getByType(validatorClass); + if (validator != null) { + break; + } + } + Assert.notNull(validator, "Could not build validator for type: " + validatorClass.getName()); + + return validator.validate(event, context, value); + + } + } +} diff --git a/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/spi/DefaultValidatorProvider.java b/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/spi/DefaultValidatorProvider.java new file mode 100644 index 000000000..4713ffb47 --- /dev/null +++ b/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/spi/DefaultValidatorProvider.java @@ -0,0 +1,51 @@ +/* + * Copyright 2011 Lincoln Baxter, III + * + * 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.ocpsoft.rewrite.annotation.spi; + +import org.ocpsoft.rewrite.bind.Validator; + +/** + * Default implementation of {@link ValidatorProvider} that builds {@link Validator} instances by creating an instance + * of the supplied class using the default constructor. + * + * @author Christian Kaltepoth + */ +public class DefaultValidatorProvider implements ValidatorProvider +{ + + @Override + public Validator getByType(Class clazz) + { + + if (Validator.class.isAssignableFrom(clazz)) { + + try { + return (Validator) clazz.newInstance(); + } + catch (InstantiationException e) { + throw new IllegalArgumentException(e); + } + catch (IllegalAccessException e) { + throw new IllegalArgumentException(e); + } + + } + + return null; + + } + +} diff --git a/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/spi/ValidatorProvider.java b/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/spi/ValidatorProvider.java new file mode 100644 index 000000000..c5c887c39 --- /dev/null +++ b/config-annotations/src/main/java/org/ocpsoft/rewrite/annotation/spi/ValidatorProvider.java @@ -0,0 +1,35 @@ +/* + * Copyright 2011 Lincoln Baxter, III + * + * 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.ocpsoft.rewrite.annotation.spi; + +import org.ocpsoft.rewrite.bind.Validator; + +/** + * SPI for providing integration with other validation frameworks. Implementations should be able to build an Rewrite + * {@link Validator} from any kind of 3rd party class. + * + * @author Christian Kaltepoth + */ +public interface ValidatorProvider +{ + + /** + * Create a Rewrite {@link Validator} from the given validator class. Which types are supported is up to the + * implementation class. A JSF implementation would for example support JSF validator. + */ + Validator getByType(Class validatorClass); + +} diff --git a/config-annotations/src/main/resources/META-INF/services/org.ocpsoft.rewrite.annotation.spi.AnnotationHandler b/config-annotations/src/main/resources/META-INF/services/org.ocpsoft.rewrite.annotation.spi.AnnotationHandler index dba5c4864..5dbe25d90 100644 --- a/config-annotations/src/main/resources/META-INF/services/org.ocpsoft.rewrite.annotation.spi.AnnotationHandler +++ b/config-annotations/src/main/resources/META-INF/services/org.ocpsoft.rewrite.annotation.spi.AnnotationHandler @@ -8,4 +8,5 @@ org.ocpsoft.rewrite.annotation.handler.RolesRequiredHandler org.ocpsoft.rewrite.annotation.handler.ParameterBindingHandler org.ocpsoft.rewrite.annotation.handler.MatchesHandler org.ocpsoft.rewrite.annotation.handler.ForwardToHandler -org.ocpsoft.rewrite.annotation.handler.ConvertHandler \ No newline at end of file +org.ocpsoft.rewrite.annotation.handler.ConvertHandler +org.ocpsoft.rewrite.annotation.handler.ValidateHandler diff --git a/config-annotations/src/main/resources/META-INF/services/org.ocpsoft.rewrite.annotation.spi.ValidatorProvider b/config-annotations/src/main/resources/META-INF/services/org.ocpsoft.rewrite.annotation.spi.ValidatorProvider new file mode 100644 index 000000000..7ba64e6c1 --- /dev/null +++ b/config-annotations/src/main/resources/META-INF/services/org.ocpsoft.rewrite.annotation.spi.ValidatorProvider @@ -0,0 +1 @@ +org.ocpsoft.rewrite.annotation.spi.DefaultValidatorProvider diff --git a/config-annotations/src/test/java/org/ocpsoft/rewrite/annotation/validate/CustomValidatorBean.java b/config-annotations/src/test/java/org/ocpsoft/rewrite/annotation/validate/CustomValidatorBean.java new file mode 100644 index 000000000..8c2b66d2c --- /dev/null +++ b/config-annotations/src/test/java/org/ocpsoft/rewrite/annotation/validate/CustomValidatorBean.java @@ -0,0 +1,30 @@ +package org.ocpsoft.rewrite.annotation.validate; + +import javax.enterprise.context.RequestScoped; +import javax.inject.Named; + +import org.ocpsoft.rewrite.annotation.Join; +import org.ocpsoft.rewrite.annotation.ParameterBinding; +import org.ocpsoft.rewrite.annotation.Validate; + +@Named +@RequestScoped +@Join(path = "/validate/{value}/", to = "/validate.jsp") +public class CustomValidatorBean +{ + + @ParameterBinding + @Validate(with = EvenLengthValidator.class) + private String value; + + public String getValue() + { + return value; + } + + public void setValue(String value) + { + this.value = value; + } + +} diff --git a/config-annotations/src/test/java/org/ocpsoft/rewrite/annotation/validate/CustomValidatorTest.java b/config-annotations/src/test/java/org/ocpsoft/rewrite/annotation/validate/CustomValidatorTest.java new file mode 100644 index 000000000..a1c20a508 --- /dev/null +++ b/config-annotations/src/test/java/org/ocpsoft/rewrite/annotation/validate/CustomValidatorTest.java @@ -0,0 +1,49 @@ +package org.ocpsoft.rewrite.annotation.validate; + +import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertEquals; + +import org.apache.http.client.methods.HttpGet; +import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.junit.Arquillian; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ocpsoft.rewrite.annotation.RewriteAnnotationTest; +import org.ocpsoft.rewrite.test.HttpAction; +import org.ocpsoft.rewrite.test.RewriteTest; +import org.ocpsoft.rewrite.test.RewriteTestBase; + +@RunWith(Arquillian.class) +public class CustomValidatorTest extends RewriteTestBase +{ + + @Deployment(testable = false) + public static WebArchive getDeployment() + { + return RewriteTest.getDeployment() + .addAsLibrary(RewriteAnnotationTest.getRewriteAnnotationArchive()) + .addAsLibrary(RewriteAnnotationTest.getRewriteCdiArchive()) + .addClasses(CustomValidatorBean.class, EvenLengthValidator.class) + .addAsWebResource(new StringAsset( + "Value: [${customValidatorBean.value}]"), + "validate.jsp"); + } + + @Test + public void testValidationWithValidValue() throws Exception + { + HttpAction action = get("/validate/abcd/"); + assertEquals(200, action.getStatusCode()); + assertTrue(action.getResponseContent().contains("Value: [abcd]")); + } + + @Test + public void testValidationWithInvalidValue() throws Exception + { + HttpAction action = get("/validate/abc/"); + assertEquals(404, action.getStatusCode()); + } + +} diff --git a/config-annotations/src/test/java/org/ocpsoft/rewrite/annotation/validate/EvenLengthValidator.java b/config-annotations/src/test/java/org/ocpsoft/rewrite/annotation/validate/EvenLengthValidator.java new file mode 100644 index 000000000..b200835aa --- /dev/null +++ b/config-annotations/src/test/java/org/ocpsoft/rewrite/annotation/validate/EvenLengthValidator.java @@ -0,0 +1,16 @@ +package org.ocpsoft.rewrite.annotation.validate; + +import org.ocpsoft.rewrite.bind.Validator; +import org.ocpsoft.rewrite.context.EvaluationContext; +import org.ocpsoft.rewrite.event.Rewrite; + +public class EvenLengthValidator implements Validator +{ + + @Override + public boolean validate(Rewrite event, EvaluationContext context, String value) + { + return value.trim().length() % 2 == 0; + } + +}