You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by ad...@apache.org on 2014/08/20 12:46:43 UTC

git commit: WICKET-5654 DefaultViolationTranslator should maybe use getMessage()

Repository: wicket
Updated Branches:
  refs/heads/master 70368f123 -> 6b62b86ae


WICKET-5654 DefaultViolationTranslator should maybe use getMessage()


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/6b62b86a
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/6b62b86a
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/6b62b86a

Branch: refs/heads/master
Commit: 6b62b86ae853150fda617e393d269c9fb9e17a77
Parents: 70368f1
Author: adelbene <an...@gmail.com>
Authored: Wed Aug 20 12:36:24 2014 +0200
Committer: adelbene <an...@gmail.com>
Committed: Wed Aug 20 12:46:22 2014 +0200

----------------------------------------------------------------------
 wicket-bean-validation/pom.xml                  |   6 +
 .../validation/DefaultViolationTranslator.java  |  71 +++++++---
 .../validation/DefaultPropertyResolverTest.java | 129 +++++++++++++++++--
 .../PasswordConstraintAnnotation.java           |  45 +++++++
 .../customconstraint/PasswordValidator.java     |  58 +++++++++
 5 files changed, 278 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/6b62b86a/wicket-bean-validation/pom.xml
----------------------------------------------------------------------
diff --git a/wicket-bean-validation/pom.xml b/wicket-bean-validation/pom.xml
index 0b52e32..b91e4e7 100644
--- a/wicket-bean-validation/pom.xml
+++ b/wicket-bean-validation/pom.xml
@@ -37,5 +37,11 @@
 			<version>5.1.2.Final</version>
 			<scope>test</scope>
 		</dependency>
+		<dependency>
+		  	<groupId>org.glassfish.web</groupId>
+		  	<artifactId>el-impl</artifactId>
+		  	<version>2.2</version>
+		  	<scope>test</scope>
+		</dependency>
 	</dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/wicket/blob/6b62b86a/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/DefaultViolationTranslator.java
----------------------------------------------------------------------
diff --git a/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/DefaultViolationTranslator.java b/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/DefaultViolationTranslator.java
index feec4f3..7b006aa 100644
--- a/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/DefaultViolationTranslator.java
+++ b/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/DefaultViolationTranslator.java
@@ -1,5 +1,8 @@
 package org.apache.wicket.bean.validation;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import javax.validation.ConstraintViolation;
 import javax.validation.metadata.ConstraintDescriptor;
 
@@ -24,37 +27,69 @@ public class DefaultViolationTranslator implements IViolationTranslator
 		ValidationError error = new ValidationError();
 		error.setMessage(violation.getMessage());
 
-		String messageKey = getMessageKey(desc);
-		if (messageKey != null)
-		{
-			if (violation.getInvalidValue() != null)
-			{
-				error.addKey(messageKey + "." +
-					violation.getInvalidValue().getClass().getSimpleName());
-			}
-			error.addKey(messageKey);
-		}
+		List<String> messages = getViolationMessages(violation, desc);
+		addErrorKeys(error, violation.getInvalidValue(), messages);
 
 		for (String key : desc.getAttributes().keySet())
 		{
 			error.setVariable(key, desc.getAttributes().get(key));
 		}
+		
 		return error;
 	}
 
-	private String getMessageKey(ConstraintDescriptor<?> desc)
+	private List<String> getViolationMessages(ConstraintViolation<?> violation,
+		ConstraintDescriptor<?> desc)
 	{
-		final Object val = desc.getAttributes().get("message");
-		if (val != null)
+		String defaultMessage = (String)desc.getAttributes().get("message");
+		String violationMessage = violation.getMessage();
+		String violationMessageTemplate = violation.getMessageTemplate();		
+		List<String> messages = new ArrayList<String>();
+
+		//violation message is considered only if it is different from
+		//the interpolated message
+		if (!Strings.isEqual(violationMessage, violationMessageTemplate))
 		{
-			String str = val.toString();
-			if (!Strings.isEmpty(str) && str.startsWith("{") && str.endsWith("}"))
+			messages.add(violationMessageTemplate);
+		}
+		
+		messages.add(violationMessage);
+		
+		//the default message is considered only if it is different from
+		//the violation message template
+		if (!Strings.isEqual(defaultMessage, violationMessageTemplate))
+		{
+			messages.add(defaultMessage);
+		}
+
+		return messages;
+	}
+
+	private void addErrorKeys(ValidationError error, Object invalidValue, List<String> messages)
+	{
+		for (String message : messages)
+		{
+			String messageKey = getMessageKey(message);
+
+			if (messageKey != null)
 			{
-				return str.substring(1, str.length() - 1);
+				if (invalidValue != null)
+				{
+					error.addKey(messageKey + "." + invalidValue.getClass().getSimpleName());
+				}
+
+				error.addKey(messageKey);
 			}
 		}
-		return null;
 	}
 
-
+	private String getMessageKey(String message)
+	{
+		if (!Strings.isEmpty(message) && message.startsWith("{") && message.endsWith("}"))
+		{
+			return message.substring(1, message.length() - 1);
+		}
+	
+		return null;
+	}
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/6b62b86a/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/DefaultPropertyResolverTest.java
----------------------------------------------------------------------
diff --git a/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/DefaultPropertyResolverTest.java b/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/DefaultPropertyResolverTest.java
index 2ef498f..1dc6802 100644
--- a/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/DefaultPropertyResolverTest.java
+++ b/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/DefaultPropertyResolverTest.java
@@ -16,12 +16,28 @@
  */
 package org.apache.wicket.bean.validation;
 
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
-
+import static org.apache.wicket.bean.validation.customconstraint.PasswordConstraintAnnotation.CUSTOM_BUNDLE_KEY;
+import static org.apache.wicket.bean.validation.customconstraint.PasswordConstraintAnnotation.DEFAULT_BUNDLE_KEY;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+import org.apache.wicket.bean.validation.customconstraint.PasswordConstraintAnnotation;
 import org.apache.wicket.markup.html.form.TextField;
 import org.apache.wicket.model.PropertyModel;
 import org.apache.wicket.util.tester.WicketTesterScope;
+import org.apache.wicket.validation.ValidationError;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -45,8 +61,7 @@ public class DefaultPropertyResolverTest
 	{
 		DefaultPropertyResolver resolver = new DefaultPropertyResolver();
 
-		TextField<?> component = new TextField<>("id", new PropertyModel<Bean1>(new Bean1(),
-			"foo"));
+		TextField<?> component = new TextField<>("id", new PropertyModel<Bean1>(new Bean1(), "foo"));
 		Property property = resolver.resolveProperty(component);
 		assertThat(property, not(nullValue()));
 		assertThat(property.getName(), is("foo"));
@@ -61,8 +76,8 @@ public class DefaultPropertyResolverTest
 	{
 		DefaultPropertyResolver resolver = new DefaultPropertyResolver();
 
-		TextField<?> component = new TextField<>("id", new PropertyModel<BooleanBean>(new BooleanBean(),
-				"foo"));
+		TextField<?> component = new TextField<>("id", new PropertyModel<BooleanBean>(
+			new BooleanBean(), "foo"));
 		Property property = resolver.resolveProperty(component);
 		assertThat(property, not(nullValue()));
 		assertThat(property.getName(), is("foo"));
@@ -74,8 +89,7 @@ public class DefaultPropertyResolverTest
 	{
 		DefaultPropertyResolver resolver = new DefaultPropertyResolver();
 
-		TextField<?> component = new TextField<>("id", new PropertyModel<Bean2>(new Bean2(),
-			"foo"));
+		TextField<?> component = new TextField<>("id", new PropertyModel<Bean2>(new Bean2(), "foo"));
 		Property property = resolver.resolveProperty(component);
 		assertThat(property, not(nullValue()));
 		assertThat(property.getName(), is("foo"));
@@ -92,8 +106,7 @@ public class DefaultPropertyResolverTest
 	{
 		DefaultPropertyResolver resolver = new DefaultPropertyResolver();
 
-		TextField<?> component = new TextField<>("id", new PropertyModel<Bean3>(new Bean3(),
-			"foo"));
+		TextField<?> component = new TextField<>("id", new PropertyModel<Bean3>(new Bean3(), "foo"));
 		Property property = resolver.resolveProperty(component);
 		assertThat(property, not(nullValue()));
 		assertThat(property.getName(), is("foo"));
@@ -108,14 +121,83 @@ public class DefaultPropertyResolverTest
 	{
 		DefaultPropertyResolver resolver = new DefaultPropertyResolver();
 
-		TextField<?> component = new TextField<>("id", new PropertyModel<Bean4>(new Bean4(),
-				"foo"));
+		TextField<?> component = new TextField<>("id", new PropertyModel<Bean4>(new Bean4(), "foo"));
 		Property property = resolver.resolveProperty(component);
 		assertThat(property, not(nullValue()));
 		assertThat(property.getName(), is("foo"));
 		assertThat(property.getOwner().getName(), is(Bean4.class.getName()));
 	}
 
+	/**
+	 * Test custom bundle mechanism of jsr 303
+	 * 
+	 * https://issues.apache.org/jira/browse/WICKET-5654
+	 * 
+	 * @throws Exception
+	 */
+	@Test
+	public void testBundleKeysResolution() throws Exception
+	{
+		ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+		Validator validator = factory.getValidator();
+		DefaultViolationTranslator translator = new DefaultViolationTranslator();
+
+		// test with a too short password
+		BeanWithPassword bean = new BeanWithPassword("short");
+
+		Set<ConstraintViolation<BeanWithPassword>> constraintViolations = validator.validate(bean);
+		assertEquals(1, constraintViolations.size());
+
+		@SuppressWarnings("unchecked")
+		ConstraintViolation<BeanWithPassword> shortViolation = (ConstraintViolation<BeanWithPassword>)constraintViolations
+			.toArray()[0];
+
+		ValidationError error = translator.convert(shortViolation);
+
+		checkErrorBundleKeys(error, DEFAULT_BUNDLE_KEY + ".String", DEFAULT_BUNDLE_KEY);
+
+		// test with a password containing non-word chars
+		bean.setPassword("notWord&%$£");
+
+		constraintViolations = validator.validate(bean);
+		assertEquals(1, constraintViolations.size());
+
+		@SuppressWarnings("unchecked")
+		ConstraintViolation<BeanWithPassword> nonWordviolation = (ConstraintViolation<BeanWithPassword>)constraintViolations.toArray()[0];
+
+		error = translator.convert(nonWordviolation);
+
+		checkErrorBundleKeys(error, CUSTOM_BUNDLE_KEY + ".String", CUSTOM_BUNDLE_KEY,
+			DEFAULT_BUNDLE_KEY + ".String", DEFAULT_BUNDLE_KEY);
+
+		// test with a valid password
+		bean.setPassword("aValidPassword1234");
+
+		constraintViolations = validator.validate(bean);
+		assertEquals(0, constraintViolations.size());
+	}
+
+	/**
+	 * Checks that validation error has the expected keys as bundle keys, in the order they are
+	 * specified in {@code expectedKeys}.
+	 * 
+	 * @param error
+	 * @param expectedKeys
+	 */
+	private void checkErrorBundleKeys(ValidationError error, String... expectedKeys)
+	{
+		List<String> keys = error.getKeys();
+
+		assertEquals("The expected number for bundle keys is '" + expectedKeys.length
+			+ "' but we have '" + keys.size() + "'", expectedKeys.length, keys.size());
+
+		for (int i = 0; i < expectedKeys.length; i++)
+		{
+			String expectedKey = expectedKeys[i];
+
+			assertTrue(keys.get(i).equals(expectedKey));
+		}
+	}
 
 	public static class Bean3
 	{
@@ -143,4 +225,25 @@ public class DefaultPropertyResolverTest
 			return "foo4";
 		}
 	}
+	
+	public static class BeanWithPassword
+	 {
+	     @PasswordConstraintAnnotation
+	     private String password;
+
+	 	public BeanWithPassword(String password)
+	 	{
+	 		this.password = password;
+	 	}
+
+	 	public String getPassword()
+	 	{
+	 		return password;
+	 	}
+
+	 	public void setPassword(String password)
+	 	{
+	 		this.password = password;
+	 	}
+	 }
 }

http://git-wip-us.apache.org/repos/asf/wicket/blob/6b62b86a/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordConstraintAnnotation.java
----------------------------------------------------------------------
diff --git a/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordConstraintAnnotation.java b/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordConstraintAnnotation.java
new file mode 100644
index 0000000..73f0ab1
--- /dev/null
+++ b/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordConstraintAnnotation.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.wicket.bean.validation.customconstraint;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+
+@Target({ METHOD, FIELD, ANNOTATION_TYPE })
+@Retention(RUNTIME)
+@Constraint(validatedBy = PasswordValidator.class)
+@Documented
+public @interface PasswordConstraintAnnotation 
+{
+	public static final String DEFAULT_BUNDLE_KEY = "default.bundle.key";
+	public static final String CUSTOM_BUNDLE_KEY = "custom.bundle.key";
+	
+	String message() default "{" + DEFAULT_BUNDLE_KEY +"}";
+
+	Class<?>[] groups() default { };
+
+	Class<? extends Payload>[] payload() default { };
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/6b62b86a/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordValidator.java
----------------------------------------------------------------------
diff --git a/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordValidator.java b/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordValidator.java
new file mode 100644
index 0000000..a319953
--- /dev/null
+++ b/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordValidator.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.wicket.bean.validation.customconstraint;
+
+import java.util.regex.Matcher;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+import org.apache.wicket.util.parse.metapattern.MetaPattern;
+import org.apache.wicket.util.string.Strings;
+
+public class PasswordValidator implements ConstraintValidator<PasswordConstraintAnnotation, String>
+{
+
+	@Override
+	public void initialize(PasswordConstraintAnnotation constraintAnnotation)
+	{
+	}
+
+	@Override
+	public boolean isValid(String value, ConstraintValidatorContext context)
+	{
+		// password must be at least 8 chars long.
+		if (Strings.isEmpty(value) || value.length() < 8)
+		{
+			return false;
+		}
+
+		Matcher matcher = MetaPattern.NON_WORD.matcher(value);
+		
+		// password must not contain non-word characters.
+		if (matcher.find())
+		{
+			context.disableDefaultConstraintViolation();
+			context.buildConstraintViolationWithTemplate("{" +
+				PasswordConstraintAnnotation.CUSTOM_BUNDLE_KEY + "}").addConstraintViolation();
+			return false;
+		}
+
+		return true;
+	}
+
+}


Re: git commit: WICKET-5654 DefaultViolationTranslator should maybe use getMessage()

Posted by Martin Grigorov <mg...@apache.org>.
On Wed, Aug 20, 2014 at 1:46 PM, <ad...@apache.org> wrote:

> Repository: wicket
> Updated Branches:
>   refs/heads/master 70368f123 -> 6b62b86ae
>
>
> WICKET-5654 DefaultViolationTranslator should maybe use getMessage()
>
>
> Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
> Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/6b62b86a
> Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/6b62b86a
> Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/6b62b86a
>
> Branch: refs/heads/master
> Commit: 6b62b86ae853150fda617e393d269c9fb9e17a77
> Parents: 70368f1
> Author: adelbene <an...@gmail.com>
> Authored: Wed Aug 20 12:36:24 2014 +0200
> Committer: adelbene <an...@gmail.com>
> Committed: Wed Aug 20 12:46:22 2014 +0200
>
> ----------------------------------------------------------------------
>  wicket-bean-validation/pom.xml                  |   6 +
>  .../validation/DefaultViolationTranslator.java  |  71 +++++++---
>  .../validation/DefaultPropertyResolverTest.java | 129 +++++++++++++++++--
>  .../PasswordConstraintAnnotation.java           |  45 +++++++
>  .../customconstraint/PasswordValidator.java     |  58 +++++++++
>  5 files changed, 278 insertions(+), 31 deletions(-)
> ----------------------------------------------------------------------
>
>
>
> http://git-wip-us.apache.org/repos/asf/wicket/blob/6b62b86a/wicket-bean-validation/pom.xml
> ----------------------------------------------------------------------
> diff --git a/wicket-bean-validation/pom.xml
> b/wicket-bean-validation/pom.xml
> index 0b52e32..b91e4e7 100644
> --- a/wicket-bean-validation/pom.xml
> +++ b/wicket-bean-validation/pom.xml
> @@ -37,5 +37,11 @@
>                         <version>5.1.2.Final</version>
>                         <scope>test</scope>
>                 </dependency>
> +               <dependency>
> +                       <groupId>org.glassfish.web</groupId>
> +                       <artifactId>el-impl</artifactId>
> +                       <version>2.2</version>
> +                       <scope>test</scope>
> +               </dependency>
>         </dependencies>
>  </project>
>
>
> http://git-wip-us.apache.org/repos/asf/wicket/blob/6b62b86a/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/DefaultViolationTranslator.java
> ----------------------------------------------------------------------
> diff --git
> a/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/DefaultViolationTranslator.java
> b/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/DefaultViolationTranslator.java
> index feec4f3..7b006aa 100644
> ---
> a/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/DefaultViolationTranslator.java
> +++
> b/wicket-bean-validation/src/main/java/org/apache/wicket/bean/validation/DefaultViolationTranslator.java
> @@ -1,5 +1,8 @@
>  package org.apache.wicket.bean.validation;
>
> +import java.util.ArrayList;
> +import java.util.List;
> +
>  import javax.validation.ConstraintViolation;
>  import javax.validation.metadata.ConstraintDescriptor;
>
> @@ -24,37 +27,69 @@ public class DefaultViolationTranslator implements
> IViolationTranslator
>                 ValidationError error = new ValidationError();
>                 error.setMessage(violation.getMessage());
>
> -               String messageKey = getMessageKey(desc);
> -               if (messageKey != null)
> -               {
> -                       if (violation.getInvalidValue() != null)
> -                       {
> -                               error.addKey(messageKey + "." +
> -
>  violation.getInvalidValue().getClass().getSimpleName());
> -                       }
> -                       error.addKey(messageKey);
> -               }
> +               List<String> messages = getViolationMessages(violation,
> desc);
> +               addErrorKeys(error, violation.getInvalidValue(), messages);
>
>                 for (String key : desc.getAttributes().keySet())
>                 {
>                         error.setVariable(key,
> desc.getAttributes().get(key));
>                 }
> +
>                 return error;
>         }
>
> -       private String getMessageKey(ConstraintDescriptor<?> desc)
> +       private List<String> getViolationMessages(ConstraintViolation<?>
> violation,
> +               ConstraintDescriptor<?> desc)
>         {
> -               final Object val = desc.getAttributes().get("message");
> -               if (val != null)
> +               String defaultMessage =
> (String)desc.getAttributes().get("message");
> +               String violationMessage = violation.getMessage();
> +               String violationMessageTemplate =
> violation.getMessageTemplate();
> +               List<String> messages = new ArrayList<String>();
> +
> +               //violation message is considered only if it is different
> from
> +               //the interpolated message
> +               if (!Strings.isEqual(violationMessage,
> violationMessageTemplate))
>                 {
> -                       String str = val.toString();
> -                       if (!Strings.isEmpty(str) && str.startsWith("{")
> && str.endsWith("}"))
> +                       messages.add(violationMessageTemplate);
> +               }
> +
> +               messages.add(violationMessage);
> +
> +               //the default message is considered only if it is
> different from
> +               //the violation message template
> +               if (!Strings.isEqual(defaultMessage,
> violationMessageTemplate))
> +               {
> +                       messages.add(defaultMessage);
> +               }
> +
> +               return messages;
> +       }
> +
> +       private void addErrorKeys(ValidationError error, Object
> invalidValue, List<String> messages)
> +       {
> +               for (String message : messages)
> +               {
> +                       String messageKey = getMessageKey(message);
> +
> +                       if (messageKey != null)
>                         {
> -                               return str.substring(1, str.length() - 1);
> +                               if (invalidValue != null)
> +                               {
> +                                       error.addKey(messageKey + "." +
> invalidValue.getClass().getSimpleName());
>

Use Classes.simpleName(invalidValue.getClass()) instead.
It is smarter when dealing with anonymous classes.


> +                               }
> +
> +                               error.addKey(messageKey);
>                         }
>                 }
> -               return null;
>         }
>
> -
> +       private String getMessageKey(String message)
> +       {
> +               if (!Strings.isEmpty(message) && message.startsWith("{")
> && message.endsWith("}"))
> +               {
> +                       return message.substring(1, message.length() - 1);
> +               }
> +
> +               return null;
> +       }
>  }
> \ No newline at end of file
>
>
> http://git-wip-us.apache.org/repos/asf/wicket/blob/6b62b86a/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/DefaultPropertyResolverTest.java
> ----------------------------------------------------------------------
> diff --git
> a/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/DefaultPropertyResolverTest.java
> b/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/DefaultPropertyResolverTest.java
> index 2ef498f..1dc6802 100644
> ---
> a/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/DefaultPropertyResolverTest.java
> +++
> b/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/DefaultPropertyResolverTest.java
> @@ -16,12 +16,28 @@
>   */
>  package org.apache.wicket.bean.validation;
>
> -import static org.hamcrest.CoreMatchers.*;
> -import static org.junit.Assert.*;
> -
> +import static
> org.apache.wicket.bean.validation.customconstraint.PasswordConstraintAnnotation.CUSTOM_BUNDLE_KEY;
> +import static
> org.apache.wicket.bean.validation.customconstraint.PasswordConstraintAnnotation.DEFAULT_BUNDLE_KEY;
> +import static org.hamcrest.CoreMatchers.is;
> +import static org.hamcrest.CoreMatchers.not;
> +import static org.hamcrest.CoreMatchers.nullValue;
> +import static org.junit.Assert.assertEquals;
> +import static org.junit.Assert.assertThat;
> +import static org.junit.Assert.assertTrue;
> +
> +import java.util.List;
> +import java.util.Set;
> +
> +import javax.validation.ConstraintViolation;
> +import javax.validation.Validation;
> +import javax.validation.Validator;
> +import javax.validation.ValidatorFactory;
> +
> +import
> org.apache.wicket.bean.validation.customconstraint.PasswordConstraintAnnotation;
>  import org.apache.wicket.markup.html.form.TextField;
>  import org.apache.wicket.model.PropertyModel;
>  import org.apache.wicket.util.tester.WicketTesterScope;
> +import org.apache.wicket.validation.ValidationError;
>  import org.junit.Rule;
>  import org.junit.Test;
>
> @@ -45,8 +61,7 @@ public class DefaultPropertyResolverTest
>         {
>                 DefaultPropertyResolver resolver = new
> DefaultPropertyResolver();
>
> -               TextField<?> component = new TextField<>("id", new
> PropertyModel<Bean1>(new Bean1(),
> -                       "foo"));
> +               TextField<?> component = new TextField<>("id", new
> PropertyModel<Bean1>(new Bean1(), "foo"));
>                 Property property = resolver.resolveProperty(component);
>                 assertThat(property, not(nullValue()));
>                 assertThat(property.getName(), is("foo"));
> @@ -61,8 +76,8 @@ public class DefaultPropertyResolverTest
>         {
>                 DefaultPropertyResolver resolver = new
> DefaultPropertyResolver();
>
> -               TextField<?> component = new TextField<>("id", new
> PropertyModel<BooleanBean>(new BooleanBean(),
> -                               "foo"));
> +               TextField<?> component = new TextField<>("id", new
> PropertyModel<BooleanBean>(
> +                       new BooleanBean(), "foo"));
>                 Property property = resolver.resolveProperty(component);
>                 assertThat(property, not(nullValue()));
>                 assertThat(property.getName(), is("foo"));
> @@ -74,8 +89,7 @@ public class DefaultPropertyResolverTest
>         {
>                 DefaultPropertyResolver resolver = new
> DefaultPropertyResolver();
>
> -               TextField<?> component = new TextField<>("id", new
> PropertyModel<Bean2>(new Bean2(),
> -                       "foo"));
> +               TextField<?> component = new TextField<>("id", new
> PropertyModel<Bean2>(new Bean2(), "foo"));
>                 Property property = resolver.resolveProperty(component);
>                 assertThat(property, not(nullValue()));
>                 assertThat(property.getName(), is("foo"));
> @@ -92,8 +106,7 @@ public class DefaultPropertyResolverTest
>         {
>                 DefaultPropertyResolver resolver = new
> DefaultPropertyResolver();
>
> -               TextField<?> component = new TextField<>("id", new
> PropertyModel<Bean3>(new Bean3(),
> -                       "foo"));
> +               TextField<?> component = new TextField<>("id", new
> PropertyModel<Bean3>(new Bean3(), "foo"));
>                 Property property = resolver.resolveProperty(component);
>                 assertThat(property, not(nullValue()));
>                 assertThat(property.getName(), is("foo"));
> @@ -108,14 +121,83 @@ public class DefaultPropertyResolverTest
>         {
>                 DefaultPropertyResolver resolver = new
> DefaultPropertyResolver();
>
> -               TextField<?> component = new TextField<>("id", new
> PropertyModel<Bean4>(new Bean4(),
> -                               "foo"));
> +               TextField<?> component = new TextField<>("id", new
> PropertyModel<Bean4>(new Bean4(), "foo"));
>                 Property property = resolver.resolveProperty(component);
>                 assertThat(property, not(nullValue()));
>                 assertThat(property.getName(), is("foo"));
>                 assertThat(property.getOwner().getName(),
> is(Bean4.class.getName()));
>         }
>
> +       /**
> +        * Test custom bundle mechanism of jsr 303
> +        *
> +        * https://issues.apache.org/jira/browse/WICKET-5654
> +        *
> +        * @throws Exception
> +        */
> +       @Test
> +       public void testBundleKeysResolution() throws Exception
> +       {
> +               ValidatorFactory factory =
> Validation.buildDefaultValidatorFactory();
> +               Validator validator = factory.getValidator();
> +               DefaultViolationTranslator translator = new
> DefaultViolationTranslator();
> +
> +               // test with a too short password
> +               BeanWithPassword bean = new BeanWithPassword("short");
> +
> +               Set<ConstraintViolation<BeanWithPassword>>
> constraintViolations = validator.validate(bean);
> +               assertEquals(1, constraintViolations.size());
> +
> +               @SuppressWarnings("unchecked")
> +               ConstraintViolation<BeanWithPassword> shortViolation =
> (ConstraintViolation<BeanWithPassword>)constraintViolations
> +                       .toArray()[0];
> +
> +               ValidationError error = translator.convert(shortViolation);
> +
> +               checkErrorBundleKeys(error, DEFAULT_BUNDLE_KEY +
> ".String", DEFAULT_BUNDLE_KEY);
> +
> +               // test with a password containing non-word chars
> +               bean.setPassword("notWord&%$£");
> +
> +               constraintViolations = validator.validate(bean);
> +               assertEquals(1, constraintViolations.size());
> +
> +               @SuppressWarnings("unchecked")
> +               ConstraintViolation<BeanWithPassword> nonWordviolation =
> (ConstraintViolation<BeanWithPassword>)constraintViolations.toArray()[0];
> +
> +               error = translator.convert(nonWordviolation);
> +
> +               checkErrorBundleKeys(error, CUSTOM_BUNDLE_KEY + ".String",
> CUSTOM_BUNDLE_KEY,
> +                       DEFAULT_BUNDLE_KEY + ".String",
> DEFAULT_BUNDLE_KEY);
> +
> +               // test with a valid password
> +               bean.setPassword("aValidPassword1234");
> +
> +               constraintViolations = validator.validate(bean);
> +               assertEquals(0, constraintViolations.size());
> +       }
> +
> +       /**
> +        * Checks that validation error has the expected keys as bundle
> keys, in the order they are
> +        * specified in {@code expectedKeys}.
> +        *
> +        * @param error
> +        * @param expectedKeys
> +        */
> +       private void checkErrorBundleKeys(ValidationError error, String...
> expectedKeys)
> +       {
> +               List<String> keys = error.getKeys();
> +
> +               assertEquals("The expected number for bundle keys is '" +
> expectedKeys.length
> +                       + "' but we have '" + keys.size() + "'",
> expectedKeys.length, keys.size());
> +
> +               for (int i = 0; i < expectedKeys.length; i++)
> +               {
> +                       String expectedKey = expectedKeys[i];
> +
> +                       assertTrue(keys.get(i).equals(expectedKey));
> +               }
> +       }
>
>         public static class Bean3
>         {
> @@ -143,4 +225,25 @@ public class DefaultPropertyResolverTest
>                         return "foo4";
>                 }
>         }
> +
> +       public static class BeanWithPassword
> +        {
> +            @PasswordConstraintAnnotation
> +            private String password;
> +
> +               public BeanWithPassword(String password)
> +               {
> +                       this.password = password;
> +               }
> +
> +               public String getPassword()
> +               {
> +                       return password;
> +               }
> +
> +               public void setPassword(String password)
> +               {
> +                       this.password = password;
> +               }
> +        }
>  }
>
>
> http://git-wip-us.apache.org/repos/asf/wicket/blob/6b62b86a/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordConstraintAnnotation.java
> ----------------------------------------------------------------------
> diff --git
> a/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordConstraintAnnotation.java
> b/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordConstraintAnnotation.java
> new file mode 100644
> index 0000000..73f0ab1
> --- /dev/null
> +++
> b/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordConstraintAnnotation.java
> @@ -0,0 +1,45 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one or more
> + * contributor license agreements.  See the NOTICE file distributed with
> + * this work for additional information regarding copyright ownership.
> + * The ASF licenses this file to You 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.apache.wicket.bean.validation.customconstraint;
> +
> +import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
> +import static java.lang.annotation.ElementType.FIELD;
> +import static java.lang.annotation.ElementType.METHOD;
> +import static java.lang.annotation.RetentionPolicy.RUNTIME;
> +
> +import java.lang.annotation.Documented;
> +import java.lang.annotation.Retention;
> +import java.lang.annotation.Target;
> +
> +import javax.validation.Constraint;
> +import javax.validation.Payload;
> +
> +@Target({ METHOD, FIELD, ANNOTATION_TYPE })
> +@Retention(RUNTIME)
> +@Constraint(validatedBy = PasswordValidator.class)
> +@Documented
> +public @interface PasswordConstraintAnnotation
> +{
> +       public static final String DEFAULT_BUNDLE_KEY =
> "default.bundle.key";
> +       public static final String CUSTOM_BUNDLE_KEY = "custom.bundle.key";
> +
> +       String message() default "{" + DEFAULT_BUNDLE_KEY +"}";
> +
> +       Class<?>[] groups() default { };
> +
> +       Class<? extends Payload>[] payload() default { };
> +}
>
>
> http://git-wip-us.apache.org/repos/asf/wicket/blob/6b62b86a/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordValidator.java
> ----------------------------------------------------------------------
> diff --git
> a/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordValidator.java
> b/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordValidator.java
> new file mode 100644
> index 0000000..a319953
> --- /dev/null
> +++
> b/wicket-bean-validation/src/test/java/org/apache/wicket/bean/validation/customconstraint/PasswordValidator.java
> @@ -0,0 +1,58 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one or more
> + * contributor license agreements.  See the NOTICE file distributed with
> + * this work for additional information regarding copyright ownership.
> + * The ASF licenses this file to You 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.apache.wicket.bean.validation.customconstraint;
> +
> +import java.util.regex.Matcher;
> +
> +import javax.validation.ConstraintValidator;
> +import javax.validation.ConstraintValidatorContext;
> +
> +import org.apache.wicket.util.parse.metapattern.MetaPattern;
> +import org.apache.wicket.util.string.Strings;
> +
> +public class PasswordValidator implements
> ConstraintValidator<PasswordConstraintAnnotation, String>
> +{
> +
> +       @Override
> +       public void initialize(PasswordConstraintAnnotation
> constraintAnnotation)
> +       {
> +       }
> +
> +       @Override
> +       public boolean isValid(String value, ConstraintValidatorContext
> context)
> +       {
> +               // password must be at least 8 chars long.
> +               if (Strings.isEmpty(value) || value.length() < 8)
> +               {
> +                       return false;
> +               }
> +
> +               Matcher matcher = MetaPattern.NON_WORD.matcher(value);
> +
> +               // password must not contain non-word characters.
> +               if (matcher.find())
> +               {
> +                       context.disableDefaultConstraintViolation();
> +                       context.buildConstraintViolationWithTemplate("{" +
> +
>  PasswordConstraintAnnotation.CUSTOM_BUNDLE_KEY +
> "}").addConstraintViolation();
> +                       return false;
> +               }
> +
> +               return true;
> +       }
> +
> +}
>
>