You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2012/12/15 03:12:56 UTC

[2/6] git commit: Recode the tapestry-beanvalidator client-side support

Recode the tapestry-beanvalidator client-side support


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/d9adc77f
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/d9adc77f
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/d9adc77f

Branch: refs/heads/5.4-js-rewrite
Commit: d9adc77fd8fc0046bf9be3dace6889e17c79b128
Parents: 395d034
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Fri Dec 14 17:57:53 2012 -0800
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Fri Dec 14 17:57:53 2012 -0800

----------------------------------------------------------------------
 tapestry-beanvalidator/build.gradle                |   27 +-
 .../modules/beanvalidator/validation.coffee        |   67 +++
 .../beanvalidator/BeanValidatorModule.java         |  115 ++++--
 .../beanvalidator/ClientConstraintDescriptor.java  |   80 ++---
 .../ClientConstraintDescriptorSource.java          |   21 +-
 .../tapestry5/internal/beanvalidator/BaseCCD.java  |   66 +++
 .../internal/beanvalidator/BeanFieldValidator.java |  312 ++++++++-------
 .../ClientConstraintDescriptorImpl.java            |   50 ++--
 .../beanvalidator/tapestry-beanvalidator.js        |   64 ---
 .../TapestryBeanValidationIntegrationTests.java    |   30 +-
 .../testapp/pages/BeanEditFormValidationDemo.java  |    2 +
 .../testapp/pages/ClientValidationDemo.java        |    2 +
 .../testapp/pages/FormClientValidationDemo.java    |   11 +-
 .../example/testapp/pages/FormValidationDemo.java  |    9 +
 .../test/java/org/example/testapp/pages/Index.java |    3 +
 .../example/testapp/pages/InjectValidatorDemo.java |    2 +
 .../org/example/testapp/pages/OnPrepareDemo.java   |    2 +
 .../src/test/webapp/FormClientValidationDemo.tml   |   56 ++--
 .../src/test/webapp/FormValidationDemo.tml         |   64 ++--
 19 files changed, 581 insertions(+), 402 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/build.gradle
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/build.gradle b/tapestry-beanvalidator/build.gradle
index bf57826..a72108b 100644
--- a/tapestry-beanvalidator/build.gradle
+++ b/tapestry-beanvalidator/build.gradle
@@ -1,3 +1,5 @@
+import t5build.*
+
 description = "Support for JSR-303 Bean Validation via the Hibernate validator implementation"
 
 dependencies {
@@ -9,8 +11,25 @@ dependencies {
   testCompile project(':tapestry-test')
 }
 
-jar {
-    manifest {
-        attributes 'Tapestry-Module-Classes': 'org.apache.tapestry5.beanvalidator.BeanValidatorModule'
+task compileCoffeeScript(type: CompileCoffeeScript) {
+    outputDir "build/compiled-coffeescript"
+}
+
+processResources {
+    from compileCoffeeScript
+}
+
+idea.module {
+    sourceDirs += compileCoffeeScript.srcDir
+    sourceDirs += compileCoffeeScript.outputDir
+
+    // Hack the IML so that "build" is not excluded; necessary because several directories under build
+    // are added as source, resources, or test folders.
+    iml.whenMerged { module ->
+        module.excludeFolders.removeAll {
+            it.canonicalUrl.endsWith "/build"
+        }
     }
-}
\ No newline at end of file
+}
+
+jar.manifest.attributes 'Tapestry-Module-Classes': 'org.apache.tapestry5.beanvalidator.BeanValidatorModule'

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/main/coffeescript/META-INF/modules/beanvalidator/validation.coffee
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/main/coffeescript/META-INF/modules/beanvalidator/validation.coffee b/tapestry-beanvalidator/src/main/coffeescript/META-INF/modules/beanvalidator/validation.coffee
new file mode 100644
index 0000000..b8eebf7
--- /dev/null
+++ b/tapestry-beanvalidator/src/main/coffeescript/META-INF/modules/beanvalidator/validation.coffee
@@ -0,0 +1,67 @@
+# Copyright 2012 The Apache Software Foundation
+#
+# 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.
+
+# ##beanvalidator/validation
+#
+# Supports extra validations related to the beanvalidator module.
+define ["_", "core/dom", "core/events", "core/utils", "core/validation"],
+  (_, dom, events, utils) ->
+
+    rangeValue = (element, attribute, defaultValue) ->
+      v = element.attribute attribute
+      if v is null
+        defaultValue
+      else
+        parseInt v
+
+    countOptions = (e) ->
+      # A select that is used as part of a palette is different; the validation attributes
+      # are attached to the selected (right side) <select>, and anything there counts as part
+      # of the selection.
+      if e.findContainer ".t-palette"
+        e.element.options.length
+      else
+        # An ordinary <select> may have multiple options (the clumsy control-click way)
+        _.filter(e.element.options, (o) -> o.selected).length
+
+    doRangeValidate = (element, value, memo) ->
+      min = rangeValue element, "data-range-min", 0
+      max = rangeValue element, "data-range-max", Number.MAX_VALUE
+
+      # If the translated value is still a string, and not a number, then the
+      # size refers to the length of the string, not its numeric value.
+      if _.isString value
+        value = value.length
+
+      unless min <= value <= max
+        memo.error = (element.attribute "data-range-message") or "RANGE ERROR"
+        return false
+
+      return true
+
+    dom.onDocument events.field.optional, "[data-optionality=prohibited]", (event, memo) ->
+
+      unless utils.isBlank memo.value
+        memo.error = (this.attribute "data-prohibited-message") or "PROHIBITED"
+        return false
+
+      return true
+
+    dom.onDocument events.field.validate, "input[data-range-min], input[data-range-max], textarea[data-range-min], textarea[data-range-max]", (event, memo) ->
+      doRangeValidate this, memo.translated, memo
+
+    dom.onDocument events.field.validate, "select[data-range-min], select[data-range-max]", (event, memo) ->
+      doRangeValidate this, (countOptions this), memo
+
+    return

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/BeanValidatorModule.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/BeanValidatorModule.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/BeanValidatorModule.java
index 16a39eb..ce75c93 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/BeanValidatorModule.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/BeanValidatorModule.java
@@ -1,4 +1,4 @@
-// Copyright 2009, 2010, 2011 The Apache Software Foundation
+// Copyright 2009, 2010, 2011, 2012 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
 // limitations under the License.
 package org.apache.tapestry5.beanvalidator;
 
-import org.apache.tapestry5.Asset;
 import org.apache.tapestry5.MarkupWriter;
 import org.apache.tapestry5.internal.beanvalidator.*;
 import org.apache.tapestry5.ioc.Configuration;
@@ -23,7 +22,8 @@ import org.apache.tapestry5.ioc.ServiceBinder;
 import org.apache.tapestry5.ioc.annotations.Local;
 import org.apache.tapestry5.ioc.services.PropertyShadowBuilder;
 import org.apache.tapestry5.ioc.services.ThreadLocale;
-import org.apache.tapestry5.services.*;
+import org.apache.tapestry5.services.FieldValidatorDefaultSource;
+import org.apache.tapestry5.services.javascript.DataConstants;
 import org.apache.tapestry5.services.javascript.JavaScriptSupport;
 
 import javax.validation.MessageInterpolator;
@@ -31,6 +31,7 @@ import javax.validation.Validator;
 import javax.validation.ValidatorFactory;
 import javax.validation.constraints.*;
 import javax.validation.groups.Default;
+import java.util.Map;
 
 /**
  * Module for JSR-303 services.
@@ -85,44 +86,100 @@ public class BeanValidatorModule
         });
     }
 
-    public static void contributeClientConstraintDescriptorSource(
-            final Configuration<ClientConstraintDescriptor> configuration)
+    public static void contributeClientConstraintDescriptorSource(final JavaScriptSupport javaScriptSupport,
+                                                                  final Configuration<ClientConstraintDescriptor> configuration)
     {
-        configuration.add(new ClientConstraintDescriptor(Max.class, "maxnumber", "value"));
-        configuration.add(new ClientConstraintDescriptor(Min.class, "minnumber", "value"));
-        configuration.add(new ClientConstraintDescriptor(NotNull.class, "notnull"));
-        configuration.add(new ClientConstraintDescriptor(Null.class, "isnull"));
-        configuration.add(new ClientConstraintDescriptor(Pattern.class, "pattern", "regexp"));
-        configuration.add(new ClientConstraintDescriptor(Size.class, "size", "min", "max"));
-    }
+        configuration.add(new BaseCCD(Max.class, "value")
+        {
+            @Override
+            public void applyClientValidation(MarkupWriter writer, String message, Map<String, Object> attributes)
+            {
+                javaScriptSupport.require("core/validation");
+                writer.attributes(
+                        "data-validate", true,
+                        "data-validate-max", attributes.get("value"),
+                        "data-max-message", message);
 
-    public void contributeMarkupRenderer(
-            OrderedConfiguration<MarkupRendererFilter> configuration,
+            }
+        });
 
-            final AssetSource assetSource,
+        configuration.add(new BaseCCD(Min.class, "value")
+        {
+            @Override
+            public void applyClientValidation(MarkupWriter writer, String message, Map<String, Object> attributes)
+            {
+                javaScriptSupport.require("core/validation");
+                writer.attributes(
+                        DataConstants.VALIDATION_ATTRIBUTE, true,
+                        "data-validate-min", attributes.get("value"),
+                        "data-min-message", message);
 
-            final ThreadLocale threadLocale,
+            }
+        });
 
-            final Environment environment)
-    {
-        MarkupRendererFilter injectBeanValidatorScript = new MarkupRendererFilter()
+        configuration.add(new BaseCCD(NotNull.class)
         {
-            public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer)
+            @Override
+            public void applyClientValidation(MarkupWriter writer, String message, Map<String, Object> attributes)
             {
+                javaScriptSupport.require("core/validation");
+                writer.attributes(
+                        DataConstants.VALIDATION_ATTRIBUTE, true,
+                        "data-optionality", "required",
+                        "data-required-message", message);
+            }
+        });
 
-                JavaScriptSupport javaScriptSupport = environment.peek(JavaScriptSupport.class);
+        configuration.add(new BaseCCD(Null.class)
+        {
+            @Override
+            public void applyClientValidation(MarkupWriter writer, String message, Map<String, Object> attributes)
+            {
+                javaScriptSupport.require("beanvalidator/validation");
+                writer.attributes(
+                        DataConstants.VALIDATION_ATTRIBUTE, true,
+                        "data-optionality", "prohibited",
+                        "data-prohibited-message", message);
+            }
+        });
 
-                Asset validators = assetSource.getAsset(null, "org/apache/tapestry5/beanvalidator/tapestry-beanvalidator.js",
-                        threadLocale.getLocale());
+        configuration.add(new BaseCCD(Pattern.class, "regexp")
+        {
+            @Override
+            public void applyClientValidation(MarkupWriter writer, String message, Map<String, Object> attributes)
+            {
+                javaScriptSupport.require("core/validation");
+                writer.attributes(
+                        DataConstants.VALIDATION_ATTRIBUTE, true,
+                        "data-validate-regexp", attributes.get("regexp"),
+                        "data-regexp-message", message);
+            }
+        });
 
-                javaScriptSupport.importJavaScriptLibrary(validators);
+        configuration.add(new BaseCCD(Size.class, "min", "max")
+        {
+            @Override
+            public void applyClientValidation(MarkupWriter writer, String message, Map<String, Object> attributes)
+            {
+                javaScriptSupport.require("beanvalidator/validation");
+                writer.attributes(
+                        DataConstants.VALIDATION_ATTRIBUTE, true,
+                        "data-range-message", message);
 
-                renderer.renderMarkup(writer);
-            }
-        };
+                int min = (Integer) attributes.get("min");
 
+                if (min != 0)
+                {
+                    writer.attributes("data-range-min", min);
+                }
 
-        configuration.add("BeanValidatorScript", injectBeanValidatorScript, "after:*");
-    }
+                int max = (Integer) attributes.get("max");
 
+                if (max != Integer.MAX_VALUE)
+                {
+                    writer.attributes("data-range-max", max);
+                }
+            }
+        });
+    }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/ClientConstraintDescriptor.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/ClientConstraintDescriptor.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/ClientConstraintDescriptor.java
index 34110bf..3e6e813 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/ClientConstraintDescriptor.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/ClientConstraintDescriptor.java
@@ -1,72 +1,50 @@
-// Copyright 2009 The Apache Software Foundation
+// Copyright 2012 The Apache Software Foundation
 //
 // 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
+// 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.tapestry5.beanvalidator;
 
-import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newSet;
+import org.apache.tapestry5.MarkupWriter;
 
+import java.util.Map;
 import java.util.Set;
 
-import org.apache.tapestry5.json.JSONObject;
-
 /**
- * Describes a single client-side constraint.
- *
+ * Applies client-side validation constraints based on a particular JSR 303 annotation.
+ * <p/>
+ * Note: converted from a final class to an interface as part of 5.4.
  */
-public final class ClientConstraintDescriptor
+public interface ClientConstraintDescriptor
 {
-   private final Class annotationClass;
-   private final String validatorName;
-   private final Set<String> attributes;
-
-   /**
-    * Creates a {@link ClientConstraintDescriptor}.
-    * 
-    * @param annotationClass Type of the constraint annotation
-    * @param validatorName Name of the client-side validator
-    * @param attributes Attribute names of the constraint annotation to be passed (along with their values) to the JavaScript validator 
-    * function as an {@link JSONObject}.
-    */
-   public ClientConstraintDescriptor(final Class annotationClass,
-         final String validatorName, final String... attributes) 
-   {
-     this.annotationClass = annotationClass;
-     this.validatorName = validatorName;
-     this.attributes = newSet(attributes);
-   }
-   
-   /**
-    * Returns the annotation describing the constraint declaration.
-    */
-   public Class getAnnotationClass() 
-   {
-     return this.annotationClass;
-   }
+    /**
+     * The annotation class that drives this descriptor.
+     */
+    Class getAnnotationClass();
 
-   /**
-    * Returns the name of the client-side validator.
-    */
-   public String getValidatorName() 
-   {
-     return this.validatorName;
-   }
+    /**
+     * Names of attributes from the {@link javax.validation.metadata.ConstraintDescriptor} that are relevant.
+     */
+    Set<String> getAttributes();
 
-   /**
-    * Attribute names of the constraint annotation to be passed (along with their values) to the JavaScript validator 
-    * function as an {@link JSONObject}. 
-    */
-   public Set<String> getAttributes() 
-   {
-     return this.attributes;
-   }
-}
\ No newline at end of file
+    /**
+     * Applies the validation
+     *
+     * @param writer
+     *         used to write new attributes into the HTML tag for the user interface element
+     * @param message
+     *         error message to present to user when the constraint is violated
+     * @param attributes
+     *         {@linkplain #getAttributes()} selected attributes} from the {@link javax.validation.metadata.ConstraintDescriptor}
+     */
+    void applyClientValidation(MarkupWriter writer, String message, Map<String, Object> attributes);
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/ClientConstraintDescriptorSource.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/ClientConstraintDescriptorSource.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/ClientConstraintDescriptorSource.java
index 1bc522a..6eadf2a 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/ClientConstraintDescriptorSource.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/beanvalidator/ClientConstraintDescriptorSource.java
@@ -1,4 +1,4 @@
-// Copyright 2010 The Apache Software Foundation
+// Copyright 2010, 2012 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -13,19 +13,20 @@
 // limitations under the License.
 package org.apache.tapestry5.beanvalidator;
 
+import org.apache.tapestry5.internal.beanvalidator.BaseCCD;
 import org.apache.tapestry5.ioc.annotations.UsesConfiguration;
 
 /**
  * Source for {@link ClientConstraintDescriptor}.
- *
  */
-@UsesConfiguration(ClientConstraintDescriptor.class)
-public interface ClientConstraintDescriptorSource 
+@UsesConfiguration(BaseCCD.class)
+public interface ClientConstraintDescriptorSource
 {
-	/**
-	 * Return a {@link ClientConstraintDescriptor} for a constraint annotation or null.
-	 * 
-	 * @param annotationClass type of the constraint annotation
-	 */
-	ClientConstraintDescriptor getConstraintDescriptor(Class annotationClass);
+    /**
+     * Return a {@link ClientConstraintDescriptor} for a constraint annotation or null if not found.
+     *
+     * @param annotationClass
+     *         type of the constraint annotation
+     */
+    ClientConstraintDescriptor getConstraintDescriptor(Class annotationClass);
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BaseCCD.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BaseCCD.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BaseCCD.java
new file mode 100644
index 0000000..78ed0de
--- /dev/null
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BaseCCD.java
@@ -0,0 +1,66 @@
+// Copyright 2009 The Apache Software Foundation
+//
+// 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.apache.tapestry5.internal.beanvalidator;
+
+import org.apache.tapestry5.beanvalidator.ClientConstraintDescriptor;
+import org.apache.tapestry5.json.JSONObject;
+
+import java.util.Set;
+
+import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newSet;
+
+/**
+ * Describes a single client-side constraint.
+ */
+public abstract class BaseCCD implements ClientConstraintDescriptor
+{
+    private final Class annotationClass;
+
+    private final Set<String> attributes;
+
+    /**
+     * Creates a {@link BaseCCD}.
+     *
+     * @param annotationClass
+     *         Type of the constraint annotation
+     * @param attributes
+     *         Attribute names of the constraint annotation to be passed (along with their values) to the JavaScript validator
+     *         function as an {@link JSONObject}.
+     */
+    public BaseCCD(Class annotationClass, String... attributes)
+    {
+        this.annotationClass = annotationClass;
+        this.attributes = newSet(attributes);
+    }
+
+    /**
+     * Returns the annotation describing the constraint declaration.
+     */
+    @Override
+    public Class getAnnotationClass()
+    {
+        return annotationClass;
+    }
+
+
+    /**
+     * Attribute names of the constraint annotation to be passed (along with their values) to the JavaScript validator
+     * function as an {@link JSONObject}.
+     */
+    @Override
+    public Set<String> getAttributes()
+    {
+        return attributes;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidator.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidator.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidator.java
index 43eedfd..81149d7 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidator.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/BeanFieldValidator.java
@@ -1,4 +1,4 @@
-// Copyright 2010 The Apache Software Foundation
+// Copyright 2010, 2012 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -13,21 +13,6 @@
 // limitations under the License.
 package org.apache.tapestry5.internal.beanvalidator;
 
-import static java.lang.String.format;
-
-import java.lang.annotation.Annotation;
-import java.util.Iterator;
-import java.util.Set;
-
-import javax.validation.ConstraintViolation;
-import javax.validation.MessageInterpolator;
-import javax.validation.Validator;
-import javax.validation.ValidatorFactory;
-import javax.validation.MessageInterpolator.Context;
-import javax.validation.metadata.BeanDescriptor;
-import javax.validation.metadata.ConstraintDescriptor;
-import javax.validation.metadata.PropertyDescriptor;
-
 import org.apache.tapestry5.Field;
 import org.apache.tapestry5.FieldValidator;
 import org.apache.tapestry5.MarkupWriter;
@@ -36,156 +21,175 @@ import org.apache.tapestry5.beanvalidator.BeanValidatorGroupSource;
 import org.apache.tapestry5.beanvalidator.ClientConstraintDescriptor;
 import org.apache.tapestry5.beanvalidator.ClientConstraintDescriptorSource;
 import org.apache.tapestry5.internal.BeanValidationContext;
-import org.apache.tapestry5.json.JSONObject;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.services.Environment;
 import org.apache.tapestry5.services.FormSupport;
 
+import javax.validation.ConstraintViolation;
+import javax.validation.MessageInterpolator;
+import javax.validation.MessageInterpolator.Context;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.PropertyDescriptor;
+import java.lang.annotation.Annotation;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import static java.lang.String.format;
+
 
 public class BeanFieldValidator implements FieldValidator
 {
-	private final Field field;
-	private final ValidatorFactory validatorFactory;
-	private final BeanValidatorGroupSource beanValidationGroupSource;
-	private final ClientConstraintDescriptorSource clientValidatorSource;
-	private final FormSupport formSupport;
-	private final Environment environment;
-	
-	public BeanFieldValidator(Field field,
-			ValidatorFactory validatorFactory,
-			BeanValidatorGroupSource beanValidationGroupSource,
-			ClientConstraintDescriptorSource clientValidatorSource,
-			FormSupport formSupport,
-			Environment environment) 
-	{
-		this.field = field;
-		this.validatorFactory = validatorFactory;
-		this.beanValidationGroupSource = beanValidationGroupSource;
-		this.clientValidatorSource = clientValidatorSource;
-		this.formSupport = formSupport;
-		this.environment = environment;
-	}
-	
-	public boolean isRequired() 
-	{
-		return false;
-	}
-
-	public void render(final MarkupWriter writer) 
-	{
-		final BeanValidationContext beanValidationContext = environment.peek(BeanValidationContext.class);
-
-		if (beanValidationContext == null) 
-		{
-			return;
-		}
-		
-		final Validator validator = validatorFactory.getValidator();
-		
-		BeanDescriptor beanDescriptor = validator.getConstraintsForClass(beanValidationContext.getBeanType());
-		
-		String currentProperty = beanValidationContext.getCurrentProperty();
-		
-		if(currentProperty == null) return;
-		
-		PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(currentProperty);
-		
-		if(propertyDescriptor == null) return;
-		
-		for (final ConstraintDescriptor<?> descriptor :propertyDescriptor.getConstraintDescriptors()) 
-		{
-			Class<? extends Annotation> annotationType = descriptor.getAnnotation().annotationType();
-			
-			ClientConstraintDescriptor clientConstraintDescriptor = clientValidatorSource.getConstraintDescriptor(annotationType);
-			
-			if(clientConstraintDescriptor != null)
-			{	
-				String message = format("%s %s", field.getLabel(), interpolateMessage(descriptor));
-				
-				JSONObject specs = new JSONObject();
-				
-                for (String attribute : clientConstraintDescriptor.getAttributes()) 
+    private final Field field;
+    private final ValidatorFactory validatorFactory;
+    private final BeanValidatorGroupSource beanValidationGroupSource;
+    private final ClientConstraintDescriptorSource clientValidatorSource;
+    private final FormSupport formSupport;
+    private final Environment environment;
+
+    public BeanFieldValidator(Field field,
+                              ValidatorFactory validatorFactory,
+                              BeanValidatorGroupSource beanValidationGroupSource,
+                              ClientConstraintDescriptorSource clientValidatorSource,
+                              FormSupport formSupport,
+                              Environment environment)
+    {
+        this.field = field;
+        this.validatorFactory = validatorFactory;
+        this.beanValidationGroupSource = beanValidationGroupSource;
+        this.clientValidatorSource = clientValidatorSource;
+        this.formSupport = formSupport;
+        this.environment = environment;
+    }
+
+    public boolean isRequired()
+    {
+        return false;
+    }
+
+    public void render(final MarkupWriter writer)
+    {
+        final BeanValidationContext beanValidationContext = environment.peek(BeanValidationContext.class);
+
+        if (beanValidationContext == null)
+        {
+            return;
+        }
+
+        final Validator validator = validatorFactory.getValidator();
+
+        BeanDescriptor beanDescriptor = validator.getConstraintsForClass(beanValidationContext.getBeanType());
+
+        String currentProperty = beanValidationContext.getCurrentProperty();
+
+        if (currentProperty == null) return;
+
+        PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(currentProperty);
+
+        if (propertyDescriptor == null) return;
+
+        for (final ConstraintDescriptor<?> descriptor : propertyDescriptor.getConstraintDescriptors())
+        {
+            Class<? extends Annotation> annotationType = descriptor.getAnnotation().annotationType();
+
+            ClientConstraintDescriptor clientConstraintDescriptor = clientValidatorSource.getConstraintDescriptor(annotationType);
+
+            if (clientConstraintDescriptor == null)
+            {
+                continue;
+            }
+
+            String message = format("%s %s", field.getLabel(), interpolateMessage(descriptor));
+
+            Map<String, Object> attributes = CollectionFactory.newMap();
+
+            for (String attribute : clientConstraintDescriptor.getAttributes())
+            {
+                Object object = descriptor.getAttributes().get(attribute);
+
+                if (object == null)
                 {
-                    Object object = descriptor.getAttributes().get(attribute);
-                    
-                    if (object == null) 
-                    {
-                      throw new RuntimeException("Expected attribute is null");
-                    }
-                    specs.put(attribute, object);
+                    throw new NullPointerException(
+                            String.format("Attribute '%s' of %s is null but is required to apply client-side validation.",
+                                    attribute, descriptor));
                 }
-                
-				formSupport.addValidation(field, clientConstraintDescriptor.getValidatorName(), message, specs);
-			}
-		}
-	}
-
-	@SuppressWarnings("unchecked")
-	public void validate(final Object value) throws ValidationException 
-	{
-
-		final BeanValidationContext beanValidationContext = environment.peek(BeanValidationContext.class);
-
-		if (beanValidationContext == null) 
-		{
-			return;
-		}
-		
-		final Validator validator = validatorFactory.getValidator();
-		
-		String currentProperty = beanValidationContext.getCurrentProperty();
-		
-		if(currentProperty == null) return;
-		
-		BeanDescriptor beanDescriptor = validator.getConstraintsForClass(beanValidationContext.getBeanType());
-		
-		PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(currentProperty);
-		
-		if(propertyDescriptor == null) return;
-		
-		final Set<ConstraintViolation<Object>> violations = validator.validateValue(
-						(Class<Object>) beanValidationContext.getBeanType(), currentProperty, 
-						value, beanValidationGroupSource.get());
-		
-		if (violations.isEmpty()) 
-		{
-			return;
-		}
-		
-		final StringBuilder builder = new StringBuilder();
-		
-		for (Iterator iterator = violations.iterator(); iterator.hasNext();) 
-		{
-			ConstraintViolation<?> violation = (ConstraintViolation<Object>) iterator.next();
-			
-			builder.append(format("%s %s", field.getLabel(), violation.getMessage()));
-			
-			if(iterator.hasNext())
-				builder.append(", ");
-	
-		}
-		
-		throw new ValidationException(builder.toString());
-
-	}
-	
-	private String interpolateMessage(final ConstraintDescriptor<?> descriptor)
-	{
-		String messageTemplate = (String) descriptor.getAttributes().get("message");
-		
-		MessageInterpolator messageInterpolator = validatorFactory.getMessageInterpolator();
-		
-		return messageInterpolator.interpolate(messageTemplate, new Context() 
-		{
-
-            public ConstraintDescriptor<?> getConstraintDescriptor() 
+                attributes.put(attribute, object);
+            }
+
+            clientConstraintDescriptor.applyClientValidation(writer, message, attributes);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    public void validate(final Object value) throws ValidationException
+    {
+
+        final BeanValidationContext beanValidationContext = environment.peek(BeanValidationContext.class);
+
+        if (beanValidationContext == null)
+        {
+            return;
+        }
+
+        final Validator validator = validatorFactory.getValidator();
+
+        String currentProperty = beanValidationContext.getCurrentProperty();
+
+        if (currentProperty == null) return;
+
+        BeanDescriptor beanDescriptor = validator.getConstraintsForClass(beanValidationContext.getBeanType());
+
+        PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(currentProperty);
+
+        if (propertyDescriptor == null) return;
+
+        final Set<ConstraintViolation<Object>> violations = validator.validateValue(
+                (Class<Object>) beanValidationContext.getBeanType(), currentProperty,
+                value, beanValidationGroupSource.get());
+
+        if (violations.isEmpty())
+        {
+            return;
+        }
+
+        final StringBuilder builder = new StringBuilder();
+
+        for (Iterator iterator = violations.iterator(); iterator.hasNext(); )
+        {
+            ConstraintViolation<?> violation = (ConstraintViolation<Object>) iterator.next();
+
+            builder.append(format("%s %s", field.getLabel(), violation.getMessage()));
+
+            if (iterator.hasNext())
+                builder.append(", ");
+
+        }
+
+        throw new ValidationException(builder.toString());
+
+    }
+
+    private String interpolateMessage(final ConstraintDescriptor<?> descriptor)
+    {
+        String messageTemplate = (String) descriptor.getAttributes().get("message");
+
+        MessageInterpolator messageInterpolator = validatorFactory.getMessageInterpolator();
+
+        return messageInterpolator.interpolate(messageTemplate, new Context()
+        {
+
+            public ConstraintDescriptor<?> getConstraintDescriptor()
             {
-              return descriptor;
+                return descriptor;
             }
 
-            public Object getValidatedValue() 
+            public Object getValidatedValue()
             {
-              return null;
+                return null;
             }
         });
-	}
+    }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/ClientConstraintDescriptorImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/ClientConstraintDescriptorImpl.java b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/ClientConstraintDescriptorImpl.java
index 4455ace..569bcaf 100644
--- a/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/ClientConstraintDescriptorImpl.java
+++ b/tapestry-beanvalidator/src/main/java/org/apache/tapestry5/internal/beanvalidator/ClientConstraintDescriptorImpl.java
@@ -1,4 +1,4 @@
-// Copyright 2010 The Apache Software Foundation
+// Copyright 2010, 2012 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -13,35 +13,35 @@
 // limitations under the License.
 package org.apache.tapestry5.internal.beanvalidator;
 
-import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newList;
+import org.apache.tapestry5.beanvalidator.ClientConstraintDescriptor;
+import org.apache.tapestry5.beanvalidator.ClientConstraintDescriptorSource;
 
 import java.util.Collection;
 
-import org.apache.tapestry5.beanvalidator.ClientConstraintDescriptorSource;
-import org.apache.tapestry5.beanvalidator.ClientConstraintDescriptor;
+import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newList;
 
-public class ClientConstraintDescriptorImpl implements ClientConstraintDescriptorSource 
+public class ClientConstraintDescriptorImpl implements ClientConstraintDescriptorSource
 {
 
-   private Collection<ClientConstraintDescriptor> descriptors = newList();
-
-   public ClientConstraintDescriptorImpl(
-         final Collection<ClientConstraintDescriptor> configuration) 
-   {
-     super();
-     this.descriptors = configuration;
-   }
-
-   public ClientConstraintDescriptor getConstraintDescriptor(final Class annotationClass) 
-   {
-     for (final ClientConstraintDescriptor desc : this.descriptors) 
-     {
-         if (desc.getAnnotationClass().equals(annotationClass)) 
-         {
-           return desc;
-         }
-     }
-     return null;
-   }
+    private Collection<ClientConstraintDescriptor> descriptors = newList();
+
+    public ClientConstraintDescriptorImpl(
+            final Collection<ClientConstraintDescriptor> configuration)
+    {
+        this.descriptors = configuration;
+    }
+
+    public ClientConstraintDescriptor getConstraintDescriptor(final Class annotationClass)
+    {
+        for (final ClientConstraintDescriptor desc : this.descriptors)
+        {
+            if (desc.getAnnotationClass().equals(annotationClass))
+            {
+                return desc;
+            }
+        }
+
+        return null;
+    }
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/main/resources/org/apache/tapestry5/beanvalidator/tapestry-beanvalidator.js
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/main/resources/org/apache/tapestry5/beanvalidator/tapestry-beanvalidator.js b/tapestry-beanvalidator/src/main/resources/org/apache/tapestry5/beanvalidator/tapestry-beanvalidator.js
deleted file mode 100644
index 07a5bd5..0000000
--- a/tapestry-beanvalidator/src/main/resources/org/apache/tapestry5/beanvalidator/tapestry-beanvalidator.js
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2010 The Apache Software Foundation
-//
-// 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.
-
-Tapestry.Validator.notnull = function(field, message, spec)
-{
-	Tapestry.Validator.required(field, message);
-};
-
-Tapestry.Validator.isnull = function(field, message, spec)
-{
-    field.addValidator(function(value)
-    {
-    	if (value != null) 
-    		throw message;
-     });
-};
-
-Tapestry.Validator.maxnumber = function(field, message, spec)
-{
-	Tapestry.Validator.max(field, message, spec.value);
-};
-
-Tapestry.Validator.minnumber = function(field, message, spec)
-{
-	Tapestry.Validator.min(field, message, spec.value);
-};
-
-Tapestry.Validator.size = function(field, message, spec)
-{
-    field.addValidator(function(value)
-    {
-    	if (Object.isString(value))
-    	{
-        	if (value.length < spec.min) throw message;
-        	if (value.length > spec.max) throw message;
-
-    	}
-    	else if (Object.isArray(value))
-    	{	
-    		if(this.tagName == "SELECT")
-    		{
-    			var selectedOptions = Element.childElements(this).size();
-    			if (selectedOptions < spec.min) throw message;
-    			if (selectedOptions > spec.max) throw message;
-    		}
-    	}
-     });
-};
-
-Tapestry.Validator.pattern = function(field, message, spec)
-{
-	Tapestry.Validator.regexp(field, message, spec.regexp);
-};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java b/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java
index 0eb48dd..29b50bf 100644
--- a/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java
+++ b/tapestry-beanvalidator/src/test/java/org/apache/tapestry5/beanvalidator/integration/TapestryBeanValidationIntegrationTests.java
@@ -21,6 +21,10 @@ import org.testng.annotations.Test;
 @TapestryTestConfiguration(webAppFolder = "src/test/webapp")
 public class TapestryBeanValidationIntegrationTests extends SeleniumTestCase
 {
+    public static final String AVAILABLE_OPTIONS = "css=.t-palette-available select";
+
+    public static final String SELECT_BUTTON = "css=.t-palette [data-action=select]";
+
     @Test
     public void form_validation() throws Exception
     {
@@ -30,6 +34,8 @@ public class TapestryBeanValidationIntegrationTests extends SeleniumTestCase
 
         clickAndWait(SUBMIT);
 
+        // waitForPageInitialized();
+
         assertTextPresent("Login Name may not be null");
         assertTextPresent("Secret Password may not be null");
         assertTextPresent("Programming Languages size must be between 2 and 3");
@@ -40,9 +46,9 @@ public class TapestryBeanValidationIntegrationTests extends SeleniumTestCase
 
         type("secretPassword", "igor");
 
-        addSelection("programmingLanguages-avail", "label=Java");
-        addSelection("programmingLanguages-avail", "label=Ruby");
-        click("programmingLanguages-select");
+        addSelection(AVAILABLE_OPTIONS, "label=Java");
+        addSelection(AVAILABLE_OPTIONS, "label=Ruby");
+        click(SELECT_BUTTON);
 
         select("favoriteColors", "label=Green");
 
@@ -106,10 +112,12 @@ public class TapestryBeanValidationIntegrationTests extends SeleniumTestCase
     }
 
     @Test
-    public void client_validaton() throws Exception
+    public void client_validation() throws Exception
     {
         openLinks("Client Validation Demo");
 
+        waitForPageInitialized();
+
         //@NotNull
         click(SUBMIT);
 
@@ -146,14 +154,18 @@ public class TapestryBeanValidationIntegrationTests extends SeleniumTestCase
 
         type("stringSizeValue", "ab");
 
-        addSelection("languages-avail", "label=Java");
-        click("languages-select");
+        click(SUBMIT);
+
+        // Have to select at least one value
+
+        addSelection(AVAILABLE_OPTIONS, "label=Ruby");
+        click(SELECT_BUTTON);
 
         click(SUBMIT);
 
         assertTextPresent("Languages size must be between 2 and 3");
 
-        click(SUBMIT);
+        waitForPageInitialized();
 
         assertTextPresent("Null Value must be null");
     }
@@ -164,14 +176,16 @@ public class TapestryBeanValidationIntegrationTests extends SeleniumTestCase
     {
         openLinks("Form Client Validation Demo");
 
+        waitForPageInitialized();
+
         click(SUBMIT);
 
         assertTextPresent("Login Name may not be null");
         assertTextPresent("Secret Password may not be null");
-        assertTextPresent("Programming Languages may not be null");
         assertTextPresent("Favorite Colors may not be null");
         assertTextPresent("Birth Day may not be null");
 
+
         type("loginName", "123");
         click(SUBMIT);
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/BeanEditFormValidationDemo.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/BeanEditFormValidationDemo.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/BeanEditFormValidationDemo.java
index 1754c7a..00f71e7 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/BeanEditFormValidationDemo.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/BeanEditFormValidationDemo.java
@@ -13,10 +13,12 @@
 // limitations under the License.
 package org.example.testapp.pages;
 
+import org.apache.tapestry5.annotations.Import;
 import org.apache.tapestry5.annotations.Persist;
 import org.apache.tapestry5.annotations.Property;
 import org.example.testapp.entities.User;
 
+@Import(stack = "core")
 public class BeanEditFormValidationDemo 
 {
 	@Property

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/ClientValidationDemo.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/ClientValidationDemo.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/ClientValidationDemo.java
index 5a42c64..14d0de1 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/ClientValidationDemo.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/ClientValidationDemo.java
@@ -13,11 +13,13 @@
 // limitations under the License.
 package org.example.testapp.pages;
 
+import org.apache.tapestry5.annotations.Import;
 import org.apache.tapestry5.annotations.Persist;
 import org.apache.tapestry5.annotations.Property;
 import org.apache.tapestry5.internal.services.StringValueEncoder;
 import org.example.testapp.entities.TestEntity;
 
+@Import(stack = "core")
 public class ClientValidationDemo
 {
     @Property

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormClientValidationDemo.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormClientValidationDemo.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormClientValidationDemo.java
index c157c5f..a5ed4ff 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormClientValidationDemo.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormClientValidationDemo.java
@@ -13,6 +13,7 @@
 // limitations under the License.
 package org.example.testapp.pages;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 
@@ -20,12 +21,14 @@ import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Pattern;
 import javax.validation.constraints.Size;
 
+import org.apache.tapestry5.annotations.Import;
 import org.apache.tapestry5.annotations.Persist;
 import org.apache.tapestry5.annotations.Property;
 import org.apache.tapestry5.beaneditor.Validate;
 import org.apache.tapestry5.internal.services.StringValueEncoder;
 import org.example.testapp.services.Foo;
 
+@Import(stack = "core")
 public class FormClientValidationDemo
 {
 	@NotNull(groups=Foo.class)
@@ -55,7 +58,13 @@ public class FormClientValidationDemo
 	@Property
 	@Persist
 	private Date date; 
-	
+
+    void onPrepare() {
+        if (languages == null) {
+            languages = new ArrayList<String>();
+        }
+    }
+
 	public StringValueEncoder getStringValueEncoder()
 	{
 		return new StringValueEncoder();

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormValidationDemo.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormValidationDemo.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormValidationDemo.java
index a5f2702..cb46348 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormValidationDemo.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/FormValidationDemo.java
@@ -13,6 +13,7 @@
 // limitations under the License.
 package org.example.testapp.pages;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 
@@ -20,12 +21,14 @@ import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Past;
 import javax.validation.constraints.Size;
 
+import org.apache.tapestry5.annotations.Import;
 import org.apache.tapestry5.annotations.Persist;
 import org.apache.tapestry5.annotations.Property;
 import org.apache.tapestry5.beaneditor.Validate;
 import org.apache.tapestry5.internal.services.StringValueEncoder;
 import org.example.testapp.services.Foo;
 
+@Import(stack = "core")
 public class FormValidationDemo
 {
 	@NotNull(groups=Foo.class)
@@ -62,6 +65,12 @@ public class FormValidationDemo
 	@Persist
 	private Date date;
 
+    public void onPrepare() {
+        if (languages == null) {
+            languages = new ArrayList<String>();
+        }
+    }
+
 	public StringValueEncoder getStringValueEncoder()
 	{
 		return new StringValueEncoder();

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/Index.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/Index.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/Index.java
index b8fb10d..307e9da 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/Index.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/Index.java
@@ -13,10 +13,13 @@
 // limitations under the License.
 package org.example.testapp.pages;
 
+import org.apache.tapestry5.annotations.Import;
+
 /**
  * Start page of the test app.
  *
  */
+@Import(stack = "core")
 public class Index 
 {
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/InjectValidatorDemo.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/InjectValidatorDemo.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/InjectValidatorDemo.java
index adae5bf..9808523 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/InjectValidatorDemo.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/InjectValidatorDemo.java
@@ -19,6 +19,7 @@ import javax.validation.ConstraintViolation;
 import javax.validation.Validator;
 import javax.validation.constraints.NotNull;
 
+import org.apache.tapestry5.annotations.Import;
 import org.apache.tapestry5.annotations.InjectComponent;
 import org.apache.tapestry5.annotations.Persist;
 import org.apache.tapestry5.annotations.Property;
@@ -27,6 +28,7 @@ import org.apache.tapestry5.corelib.components.Form;
 import org.apache.tapestry5.ioc.annotations.Inject;
 import org.example.testapp.services.Bar;
 
+@Import(stack = "core")
 public class InjectValidatorDemo 
 {
 	@NotNull(groups=Bar.class)

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/OnPrepareDemo.java
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/OnPrepareDemo.java b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/OnPrepareDemo.java
index 42510e0..1453645 100644
--- a/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/OnPrepareDemo.java
+++ b/tapestry-beanvalidator/src/test/java/org/example/testapp/pages/OnPrepareDemo.java
@@ -13,9 +13,11 @@
 // limitations under the License.
 package org.example.testapp.pages;
 
+import org.apache.tapestry5.annotations.Import;
 import org.apache.tapestry5.annotations.Property;
 import org.example.testapp.entities.User;
 
+@Import(stack = "core")
 public class OnPrepareDemo 
 {
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/test/webapp/FormClientValidationDemo.tml
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/webapp/FormClientValidationDemo.tml b/tapestry-beanvalidator/src/test/webapp/FormClientValidationDemo.tml
index 344246c..bdcce37 100644
--- a/tapestry-beanvalidator/src/test/webapp/FormClientValidationDemo.tml
+++ b/tapestry-beanvalidator/src/test/webapp/FormClientValidationDemo.tml
@@ -1,28 +1,32 @@
 <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">
-    <body>
-        <t:form>
-        	<t:errors/>
-        	
-        	<br/>
-        	
-        	<t:textfield t:id="loginName" value="userName"/>
-        	
-        	<br/>
-        	
-        	<t:textfield t:id="secretPassword" value="password"/>
-        	
-        	<br/>
-        	<t:palette t:id="programmingLanguages" model="literal:Java,C,Ruby,Python,Perl" selected="languages" encoder="stringValueEncoder"/>       
-        		
-        	<br/>
-        	<t:select t:id="favoriteColors" model="literal:Red,Green,Blue" value="color"/>   
-        	     	
-        	<br/>
-        	<t:datefield t:id="birthDay" value="date" format="dd.MM.yyyy"/>
-
-        	<br/>
-        	<input type="submit" value="Go"/>
-        	
-        </t:form>
-    </body>
+<body>
+<div class="container">
+
+    <t:form>
+        <t:errors/>
+
+        <br/>
+
+        <t:textfield t:id="loginName" value="userName"/>
+
+        <br/>
+
+        <t:textfield t:id="secretPassword" value="password"/>
+
+        <br/>
+        <t:palette t:id="programmingLanguages" model="literal:Java,C,Ruby,Python,Perl" selected="languages"
+                   encoder="stringValueEncoder"/>
+
+        <br/>
+        <t:select t:id="favoriteColors" model="literal:Red,Green,Blue" value="color"/>
+
+        <br/>
+        <t:datefield t:id="birthDay" value="date" format="dd.MM.yyyy"/>
+
+        <br/>
+        <input type="submit" value="Go"/>
+
+    </t:form>
+</div>
+</body>
 </html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/d9adc77f/tapestry-beanvalidator/src/test/webapp/FormValidationDemo.tml
----------------------------------------------------------------------
diff --git a/tapestry-beanvalidator/src/test/webapp/FormValidationDemo.tml b/tapestry-beanvalidator/src/test/webapp/FormValidationDemo.tml
index 380283e..2701ea5 100644
--- a/tapestry-beanvalidator/src/test/webapp/FormValidationDemo.tml
+++ b/tapestry-beanvalidator/src/test/webapp/FormValidationDemo.tml
@@ -1,32 +1,36 @@
 <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">
-    <body>
-        <t:form clientValidation="none">
-        	<t:errors/>
-        	
-        	<br/>
-        	
-        	<t:textfield t:id="loginName" value="userName"/>
-        	
-        	<br/>
-        	
-        	<t:textfield t:id="secretPassword" value="password"/>
-        	
-        	<br/>
-        	<t:palette t:id="programmingLanguages" model="literal:Java,C,Ruby,Python,Perl" selected="languages" encoder="stringValueEncoder"/>
-        	
-        	<br/>
-        	<t:select t:id="favoriteColors" model="literal:Red,Green,Blue" value="color"/>
-
-            <br/>
-            <label t:type="label" for="moreColors"/>
-        	<t:checklist t:id="moreColors" model="literal:White,Yellow,Orange,Pink" selected="moreColors" encoder="stringValueEncoder"/>
-        	
-        	<br/>
-        	<t:datefield t:id="birthDay" value="date" format="dd.MM.yyyy"/>
-
-        	<br/>
-        	<input type="submit" value="Go"/>
-        	
-        </t:form>
-    </body>
+<body>
+<div class="container">
+    <t:form clientValidation="none">
+        <t:errors/>
+
+        <br/>
+
+        <t:textfield t:id="loginName" value="userName"/>
+
+        <br/>
+
+        <t:textfield t:id="secretPassword" value="password"/>
+
+        <br/>
+        <t:palette t:id="programmingLanguages" model="literal:Java,C,Ruby,Python,Perl" selected="languages"
+                   encoder="stringValueEncoder"/>
+
+        <br/>
+        <t:select t:id="favoriteColors" model="literal:Red,Green,Blue" value="color"/>
+
+        <br/>
+        <label t:type="label" for="moreColors"/>
+        <t:checklist t:id="moreColors" model="literal:White,Yellow,Orange,Pink" selected="moreColors"
+                     encoder="stringValueEncoder"/>
+
+        <br/>
+        <t:datefield t:id="birthDay" value="date" format="dd.MM.yyyy"/>
+
+        <br/>
+        <input type="submit" value="Go"/>
+
+    </t:form>
+</div>
+</body>
 </html>
\ No newline at end of file