[maven-release-plugin]  copy for tag 0.4

git-svn-id: http://svn.apache.org/repos/asf/bval/tags/0.4@1311345 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/trunk/CHANGES.txt b/trunk/CHANGES.txt
new file mode 100644
index 0000000..56144d3
--- /dev/null
+++ b/trunk/CHANGES.txt
@@ -0,0 +1,214 @@
+Apache BVal (incubating) - CHANGES.txt
+Licensed under Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0
+--------------------------------------------------------------------------------
+
+Content
+=======
+   * Overview
+   * Disclaimer
+   * Prerequisites
+   * Documentation
+   * Getting Involved
+   * Release Notes
+
+
+Overview
+========
+This is an implementation of JSR 303 (Bean Validation), a specification of the
+Java API for Javabean validation in Java EE and Java SE.
+The technical objective is to provide a class level constraint declaration and
+validation facility for the Java application developer, as well as a constraint
+metadata repository and query API.
+
+This implementation is based on the validation framework of agimatec GmbH,
+which was contributed to the ASF under a software grant.
+
+
+Disclaimer
+==========
+Apache BVal is an effort undergoing incubation at The Apache Software
+Foundation (ASF), sponsored by the Apache Incubator PMC. Incubation is required
+of all newly accepted projects until a further review indicates that the
+infrastructure, communications, and decision making process have stabilized in a
+manner consistent with other successful ASF projects. While incubation status is
+not necessarily a reflection of the completeness or stability of the code, it
+does indicate that the project has yet to be fully endorsed by the ASF.
+
+
+Prerequisites
+=============
+You need a platform that supports Java SE 5 or later.
+
+
+Documentation
+=============
+Please refer to the project page at:
+    http://incubator.apache.org/bval/
+
+
+Getting Involved
+================
+The Apache BVal project is being built by the open source community
+for the open source community - we welcome your input and contributions!
+
+What are we looking for?
+  * Source code and fixes contributions
+  * Documentation assistance
+  * Project and feature suggestions
+  * Integration and testing with other projects
+  * Detailed and constructive feedback
+  * Articles and whitepapers
+
+How do I Contribute?
+  * To discuss BVal topics, check out the mailing lists.
+  * Bugs and other issues can be posted on the project JIRA.
+
+
+Release Notes - BVal - Version 0.4
+==================================
+
+Bug
+---
+    * [BVAL-54] - Handle Java security policies and managers
+    * [BVAL-63] - use java.util.ServiceLoader in DefaultValidationProviderResolver#getValidationProviders()
+    * [BVAL-89] - Application specific constraints and artifacts not visible by provider in OSGI-based app server
+    * [BVAL-90] - Inconsistent fraction handling of @Digits when target is a BigDecimal
+    * [BVAL-91] - Security hole in org.apache.bval.util.MethodAccess.get()
+    * [BVAL-92] - Security holes in org.apache.bval.util.PrivilegedActions
+    * [BVAL-94] - Missing SVN EOL properties
+    * [BVAL-95] - Beanvalidation with Generic-DataTypes
+    * [BVAL-96] - Use correct encoding for localization messages
+    * [BVAL-97] - More than one META-INF/validation.xml in tomcat webapp
+    * [BVAL-100] - Java2 Security AccessControlException after moving to use common-lang3
+    * [BVAL-104] - german validation messages properties are broken
+
+Improvement
+-----------
+    * [BVAL-68] - Use FindBugs to reduce coding errors
+
+Task
+----
+    * [BVAL-99] - upgrade to apache parent pom 10
+    * [BVAL-103] - switch BVal from slf4j to java.util.logging
+
+
+Release Notes for 0.3-incubating
+================================
+
+Bug
+---
+    * [BVAL-84] - CNFEX org.slf4j.impl.StaticLoggerBinder building xstream and guice
+    * [BVAL-86] - Missing DecimalMax/DecimalMin constraint messages in ValidationMessages.properties
+    * [BVAL-87] - Java 2 security violations in ClassValidator.validate
+    * [BVAL-88] - Cascaded validation adds a constraint violation for valid child property
+
+Improvement
+-----------
+    * [BVAL-60] - Use SLF4J API for logging
+    * [BVAL-85] - bval-core has no dependency on slf4j-jcl*
+
+
+Release Notes for 0.2-incubating
+================================
+
+Bug
+---
+    * [BVAL-47] - Correctly follow GroupSequence definitions when validating
+    * [BVAL-70] - Remove @Override annotations from methods implementing interfaces
+    * [BVAL-71] - Compile errors with Java SE 5
+    * [BVAL-72] - NPE when doing methodvalidation
+    * [BVAL-77] - Recent changes made the bval-xstream module required, instead of optional
+
+Improvement
+-----------
+    * [BVAL-59] - Write or reuse an scm-plugin mojo to capture the svn revision
+    * [BVAL-69] - make dependency to com.thoughtworks.xstream of bval-core optional or obsolete
+    * [BVAL-73] - Alternative way of addressing code reuse issue between the validators
+    * [BVAL-74] - Remove redundant method call from ClassValidator.validateProperty()
+    * [BVAL-75] - javadocs, compiler (generics) warnings, etc.
+    * [BVAL-76] - Correct spelling error in API:  ConstraintValidationListener.getConstaintViolations()
+    * [BVAL-78] - Avoid unsafe casting and object swapping when processing @ReportAsSingleViolation composed constraints
+    * [BVAL-79] - email validation can easily work for any CharSequence rather than String only
+
+
+Release Notes for 0.1-incubating
+================================
+
+Sub-task
+--------
+    * [BVAL-3] - Move all copyrights to NOTICE files
+    * [BVAL-4] - Rename java packages
+    * [BVAL-5] - Update builds for incubator
+    * [BVAL-7] - Setup initial structure/navigation/content in the wiki
+    * [BVAL-8] - Setup confluence autoexport template
+    * [BVAL-9] - Setup rsync cron job to copy autoexport content over to www staging area
+
+Bug
+---
+    * [BVAL-11] - After moved java packages tests don't work
+    * [BVAL-13] - ConstraintViolationImpl must be Serialziable according to JSR303 spec
+    * [BVAL-15] - @Min and @Max fail with very high values of long
+    * [BVAL-17] - PathImpl keeps a null-named first node on unindexed property paths
+    * [BVAL-18] - DefaultMessageInterpolator substitutes unexistant variables with the whole message
+    * [BVAL-19] - Several changes to pass 12 more tests
+    * [BVAL-20] - When building a MetaBean, the full tree of subclasses/interfaces must be considered
+    * [BVAL-21] - ConstraintViolations set should not have duplicates and should store ElementType and use it in equals()
+    * [BVAL-22] - Incorrect output or IOBException when interpolating messages when annotation values have $ or \
+    * [BVAL-23] - Circular dependency check produces false positives
+    * [BVAL-24] - IllegalArgumentException should be thrown when calling Validator.getConstraintsForClass() with a null class
+    * [BVAL-26] - IllegalAccessException is thrown when accesing annotation values
+    * [BVAL-27] - IllegalArgumentException should be thrown when calling BeanDescriptor.getConstraintsForProperty(null)
+    * [BVAL-28] - Property path for nodes inside iterables that do not implement List should not contain the index
+    * [BVAL-29] - Defer node creation in implementation of ConstraintViolationBuilder API
+    * [BVAL-30] - Groups from the main constraint annotation must be inherited by the composing annotations
+    * [BVAL-31] - A ConstraintDefinitionException should be thrown when the resolved validator is not compatible with the annotation type
+    * [BVAL-32] - Annotations associated with composited contraints must reflect inherited groups
+    * [BVAL-33] - Payload value must also be inherited in compositing constraints
+    * [BVAL-34] - Parameter for @Pattern message should be {regexp}
+    * [BVAL-35] - Improved support of @OverridesAttribute
+    * [BVAL-36] - Improve @ReportAsSingleValidation implementation
+    * [BVAL-37] - Ensure constraint definitions are valid
+    * [BVAL-38] - Groups and payload values must be part of the ConstraintDescriptor attributes
+    * [BVAL-39] - Implicit groups should be correctly set in constraint descriptors
+    * [BVAL-41] - Package bval artifacts as bundles
+    * [BVAL-42] - Stop group sequence validation when at least 1 violation is already reported
+    * [BVAL-43] - GroupDefinitionException must be thrown on cyclic group detection
+    * [BVAL-45] - ValidationException must be thrown when validating a bean with an annotated method which isn't a valid getter
+    * [BVAL-48] - jUnit failure during build on Windows - trunk rev 945800
+    * [BVAL-49] - ValidationException must be thrown in case ConstraintValidatorFactory returns null for a valid getInstance call
+    * [BVAL-50] - Fix TraversableResolver usage
+    * [BVAL-51] - Several fixes in ElementDescriptor and subinterfaces impls
+    * [BVAL-53] - Several fixes for XML metadata processing impl
+    * [BVAL-55] - Fix remaining TCK tests
+    * [BVAL-58] - Clean up build before release
+    * [BVAL-62] - add missing ASL headers
+    * [BVAL-64] - org.apache.bval.bundle needs a custom NOTICE file
+    * [BVAL-65] - Incubator required DISCLAIMER missing from jars
+    * [BVAL-67] - Resolve NOTICE file issues fond during RC2 vote
+
+Improvement
+-----------
+    * [BVAL-12] - Current implementation of method validation (appendix C of JSR-303) doesn't work with some constraints
+    * [BVAL-16] - Maven project to launch TCK suite
+    * [BVAL-44] - Move optional JSON code out of bval-core to a bval-json module
+    * [BVAL-46] - Create project source assembly
+    * [BVAL-52] - ValidationParser.getInputStream() should call getResourceAsStream() for TCK suite to work
+    * [BVAL-57] - Create a single apache-bval jar/bundle
+    * [BVAL-66] - Method validation parameter processing problems
+
+New Feature
+-----------
+    * [BVAL-40] - Provide a way to display the BVal version being used
+    * [BVAL-56] - make bval work with geronimo spec osgi service locator
+
+Task
+----
+    * [BVAL-2] - Initial codebase donation tasks
+    * [BVAL-6] - Setup project website
+
+Test
+----
+    * [BVAL-10] - Setup continuous builds
+
+--------------------------------------------------------------------------------
+
diff --git a/trunk/LICENSE b/trunk/LICENSE
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/trunk/LICENSE
@@ -0,0 +1,203 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
+
diff --git a/trunk/NOTICE b/trunk/NOTICE
new file mode 100644
index 0000000..28b288f
--- /dev/null
+++ b/trunk/NOTICE
@@ -0,0 +1,14 @@
+
+Apache Incubator - BVal project
+Copyright 2010-2012 The Apache Software Foundation.
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+
+
+The following copyright notice(s) were affixed to portions of this code
+with which this file is now or was at one time distributed.
+
+This product includes software developed by Agimatec GmbH.
+Copyright 2007-2010 Agimatec GmbH. All rights reserved.
+
diff --git a/trunk/README.txt b/trunk/README.txt
new file mode 100644
index 0000000..5047689
--- /dev/null
+++ b/trunk/README.txt
@@ -0,0 +1,66 @@
+Apache BVal - README.txt
+Licensed under Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0
+--------------------------------------------------------------------------------
+
+About
+=====
+This is an implementation of JSR 303 (Bean Validation), a specification of the
+Java API for Javabean validation in Java EE and Java SE.
+The technical objective is to provide a class level constraint declaration and
+validation facility for the Java application developer, as well as a constraint
+metadata repository and query API.
+
+
+Release Notes
+=============
+Please refer to the included CHANGES.txt for a complete list of new features
+available and issues resolved in this release.
+
+
+Getting Started
+===============
+Please visit the project website for the latest information:
+    http://bval.apache.org/
+
+Along with the developer mailing list archive:
+    http://mail-archives.apache.org/mod_mbox/bval-dev/
+
+
+System Requirements
+===================
+You need a platform that supports Java SE 5 or later.
+Most testing has been done with Java SE 6 on Mac OS X, Windows and Linux.
+
+
+Building
+==========================
+To build from source code:
+  - Sources require Java SE 5 or higher. (Tested with JDK 1.5.0_22 and 1.6.0_21)
+  - The project is built with Apache Maven (suggested is at least 2.2.1).
+    You need to download and install Maven from: http://maven.apache.org/
+  - Invoke maven in the root directory or a module subdirectory:
+      mvn install
+
+Checkout the Building section on the project web site for more details:
+    http://bval.apache.org/
+
+
+Support
+=======
+Any problems with this release can be reported to the user mailing list or
+opened in the Jira issue tracker.
+
+  Mailing list archive:
+    http://mail-archives.apache.org/mod_mbox/bval-user/
+
+  User mailing list:
+    user@bval.apache.org
+
+  User mailing list subscription:
+    user-subscribe@bval.apache.org
+
+  Jira:
+    https://issues.apache.org/jira/browse/BVAL/
+
+--------------------------------------------------------------------------------
+
diff --git a/trunk/bundle/pom.xml b/trunk/bundle/pom.xml
new file mode 100644
index 0000000..dd7a55b
--- /dev/null
+++ b/trunk/bundle/pom.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<!--
+	Maven release plugin requires the project tag to be on a single line.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.bval</groupId>
+        <artifactId>bval-parent</artifactId>
+        <version>0.4</version>
+    </parent>
+
+    <!-- use fully qualified naming for OSGi bundles -->
+    <artifactId>org.apache.bval.bundle</artifactId>
+    <name>Apache BVal :: org.apache.bval.bundle</name>
+    <packaging>bundle</packaging>
+
+    <description>Apache BVal - Aggregate OSGi Bundle (bval-core + bval-jsr303)</description>
+
+    <dependencies>
+        <!-- JARs to include in aggregate JAR via maven-shade-plugin -->
+        <dependency>
+            <groupId>org.apache.bval</groupId>
+            <artifactId>bval-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.bval</groupId>
+            <artifactId>bval-jsr303</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <!-- OSGi Bundle Metadata -->
+                        <Main-Class>org.apache.bval.util.BValVersion</Main-Class>
+                        <Implementation-Title>Apache BVal</Implementation-Title>
+                        <Implementation-Build>${buildNumber}</Implementation-Build>
+                        <Specification-Title>JSR-303 Bean Validation</Specification-Title>
+                        <Specification-Vendor>Sun Microsystems, Inc.</Specification-Vendor>
+                        <Specification-Version>1.0</Specification-Version>
+                        <Bundle-DocURL>${project.url}</Bundle-DocURL>
+                        <Embed-Dependency>*;inline=true</Embed-Dependency>
+                        <Private-Package />
+                        <Export-Package>org.apache.bval.*</Export-Package>
+                        <Import-Package>javax.persistence.*;resolution:=optional,org.apache.bval.xml.*;resolution:=optional,*</Import-Package>
+                        <!-- geronimo spec osgi services support -->
+                        <SPI-Provider>true</SPI-Provider>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>apache-release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-javadoc-plugin</artifactId>
+                        <configuration>
+                            <skip>true</skip>
+                        </configuration>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-source-plugin</artifactId>
+                        <configuration>
+                            <attach>false</attach>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
+
diff --git a/trunk/bundle/src/main/appended-resources/META-INF/NOTICE.vm b/trunk/bundle/src/main/appended-resources/META-INF/NOTICE.vm
new file mode 100644
index 0000000..05ddae6
--- /dev/null
+++ b/trunk/bundle/src/main/appended-resources/META-INF/NOTICE.vm
@@ -0,0 +1,26 @@
+##
+## 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.
+##
+
+
+The following copyright notice(s) were affixed to portions of this code
+with which this file is now or was at one time distributed.
+
+This product includes software developed by agimatec GmbH.
+Copyright 2007-2010 Agimatec GmbH. All rights reserved.
+
diff --git a/trunk/bval-core/pom.xml b/trunk/bval-core/pom.xml
new file mode 100644
index 0000000..5251d67
--- /dev/null
+++ b/trunk/bval-core/pom.xml
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<!--
+	Maven release plugin requires the project tag to be on a single line.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.bval</groupId>
+        <artifactId>bval-parent</artifactId>
+        <version>0.4</version>
+    </parent>
+
+    <artifactId>bval-core</artifactId>
+    <name>Apache BVal :: bval-core</name>
+    <packaging>jar</packaging>
+
+    <description>BVal Metadata Engine</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!--
+                get the svn revision number and project version
+                and set it in a properties file for later retrieval
+            -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>set subversion revision</id>
+                        <phase>compile</phase>
+                        <configuration>
+                            <tasks>
+                                <echo>Version: ${project.version}</echo>
+                                <echo>Revision: ${buildNumber}</echo>
+                                <echo>Date: ${timestamp}</echo>
+                                <mkdir dir="${project.build.outputDirectory}/META-INF" />
+                                <echo file="${project.build.outputDirectory}/META-INF/org.apache.bval.revision.properties">
+# Licensed under Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0
+project.name=Apache BVal
+project.version=${project.version}
+svn.revision=${buildNumber}
+svn.branch=${scmBranch}
+build.timestamp=${timestamp}
+                                </echo>
+                            </tasks>
+                        </configuration>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <!-- create mainClass attribute -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>default-jar</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                        <configuration>
+                            <archive>
+                                <manifest>
+                                   <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                                   <mainClass>org.apache.bval.util.BValVersion</mainClass>
+                                </manifest>
+                                <manifestEntries>
+                                    <Implementation-Build>${buildNumber}</Implementation-Build>
+                                </manifestEntries>
+                            </archive>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>attach-tests</id>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+        <pluginManagement>
+        	<plugins>
+        		<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
+        		<plugin>
+        			<groupId>org.eclipse.m2e</groupId>
+        			<artifactId>lifecycle-mapping</artifactId>
+        			<version>1.0.0</version>
+        			<configuration>
+        				<lifecycleMappingMetadata>
+        					<pluginExecutions>
+        						<pluginExecution>
+        							<pluginExecutionFilter>
+        								<groupId>
+        									org.apache.maven.plugins
+        								</groupId>
+        								<artifactId>
+        									maven-antrun-plugin
+        								</artifactId>
+        								<versionRange>
+        									[1.3,)
+        								</versionRange>
+        								<goals>
+        									<goal>run</goal>
+        								</goals>
+        							</pluginExecutionFilter>
+        							<action>
+        								<ignore />
+        							</action>
+        						</pluginExecution>
+        					</pluginExecutions>
+        				</lifecycleMappingMetadata>
+        			</configuration>
+        		</plugin>
+        	</plugins>
+        </pluginManagement>
+    </build>
+</project>
+
diff --git a/trunk/bval-core/src/main/appended-resources/META-INF/NOTICE.vm b/trunk/bval-core/src/main/appended-resources/META-INF/NOTICE.vm
new file mode 100644
index 0000000..9a0c1ee
--- /dev/null
+++ b/trunk/bval-core/src/main/appended-resources/META-INF/NOTICE.vm
@@ -0,0 +1,25 @@
+##
+## 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.
+##
+
+The following copyright notice(s) were affixed to portions of this code
+with which this file is now or was at one time distributed.
+
+This product includes software developed by agimatec GmbH.
+Copyright 2007-2010 Agimatec GmbH. All rights reserved.
+
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/BeanValidationContext.java b/trunk/bval-core/src/main/java/org/apache/bval/BeanValidationContext.java
new file mode 100644
index 0000000..b9b8373
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/BeanValidationContext.java
@@ -0,0 +1,331 @@
+/*
+ *  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.bval;
+
+import org.apache.bval.model.*;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.PropertyAccess;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * Description: Context during validation to help the {@link org.apache.bval.model.Validation}
+ * and the {@link BeanValidator} do their jobs.
+ * Used to bundle {@link BeanValidationContext} and {@link ValidationListener}
+ * together <br/>
+ * <b>This class is NOT thread-safe: a new instance will be created for each
+ * validation
+ * processing per thread.<br/></b>
+ */
+public class BeanValidationContext<T extends ValidationListener>
+      implements ValidationContext<T> {
+    /** represent an unknown propertyValue. */
+    private static final Object UNKNOWN = new Object() {
+        public String toString() {
+            return "unknown property value";
+        };
+    };
+
+    /** metainfo of current object. */
+    private MetaBean metaBean;
+    /** current object. */
+    private Object bean;
+    /** metainfo of current property. */
+    private MetaProperty metaProperty;
+    /**
+     * cached value of current property.
+     * Cached because of potential redundant access for different Validations
+     */
+    private Object propertyValue = UNKNOWN;
+
+    /** access strategy used for previous access */
+    private AccessStrategy access;
+
+    /** set of objects already validated to avoid endless loops. */
+    @SuppressWarnings({ "rawtypes" })
+    protected Map validatedObjects;
+
+    /**
+     * true when value is fixed, so that it will NOT be dynamically
+     * determined from the annotated element or the metaProperty.
+     * <b><br>Note: When value is UNKNOWN, it will be determined THE FIRST TIME
+     * IT IS ACCESSED.</b>
+     */
+    private boolean fixed;
+
+    /** listener notified of validation constraint violations. */
+    private T listener;
+
+    /**
+     * Create a new BeanValidationContext instance.
+     * @param listener
+     */
+    @SuppressWarnings({ "rawtypes" })
+    public BeanValidationContext(T listener) {
+        this(listener, new IdentityHashMap());
+    }
+
+    /**
+     * Create a new BeanValidationContext instance.
+     * @param listener
+     * @param validatedMap
+     */
+    @SuppressWarnings({ "rawtypes" })
+    protected BeanValidationContext(T listener, Map validatedMap) {
+        this.listener = listener;
+        this.validatedObjects = validatedMap;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T getListener() {
+        return listener;
+    }
+
+    /**
+     * Set the listener.
+     * @param listener T
+     */
+    public void setListener(T listener) {
+        this.listener = listener;
+    }
+
+    /**
+     * {@inheritDoc}
+     * Here, state equates to a given bean reference.
+     */
+    @SuppressWarnings("unchecked")
+    public boolean collectValidated() {
+        return validatedObjects.put(getBean(), Boolean.TRUE) == null;
+    }
+
+    /**
+     * Learn whether a particular object has been validated.
+     * @param object
+     * @return true when the object has already been validated in this context
+     */
+    public boolean isValidated(Object object) {
+        return validatedObjects.containsKey(object);
+    }
+
+    /**
+     * Clear map of validated objects (invoke when you want to 'reuse' the
+     * context for different validations)
+     */
+    public void resetValidated() {
+        validatedObjects.clear();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setBean(Object aBean, MetaBean aMetaBean) {
+        bean = aBean;
+        metaBean = aMetaBean;
+        metaProperty = null;
+        unknownValue();
+    }
+
+    /**
+     * Get the cached value or access it somehow (via field or method)<br>
+     * <b>you should prefer getPropertyValue(AccessStrategy) instead of this method</b>
+     *
+     * @return the current value of the property accessed by reflection
+     * @throws IllegalArgumentException - error accessing attribute (config error, reflection problem)
+     * @throws IllegalStateException    - when no property is currently set in the context (application logic bug)
+     */
+    public Object getPropertyValue() {
+        if (access == null) { // undefined access strategy
+            return getPropertyValue(
+                  new PropertyAccess(bean.getClass(), metaProperty.getName()));
+        } else {
+            return getPropertyValue(access);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * Caches retrieved value.
+     */
+    public Object getPropertyValue(AccessStrategy access)
+          throws IllegalArgumentException, IllegalStateException {
+        if (propertyValue == UNKNOWN || (this.access != access && !fixed)) {
+            propertyValue = access.get(bean);
+            this.access = access;
+        }
+        return propertyValue;
+    }
+
+    /**
+     * Convenience method to access metaProperty.name
+     *
+     * @return null or the name of the current property
+     */
+    public String getPropertyName() {
+        return metaProperty == null ? null : metaProperty.getName();
+    }
+
+    /**
+     * Set the current property value.
+     * @param propertyValue
+     */
+    public void setPropertyValue(Object propertyValue) {
+        this.propertyValue = propertyValue;
+    }
+
+    /**
+     * Set the property value, fixed.
+     * @param value
+     */
+    public void setFixedValue(Object value) {
+        setPropertyValue(value);
+        setFixed(true);
+    }
+
+    /**
+     * Learn whether the current property value is "fixed."
+     * @return boolean
+     */
+    public boolean isFixed() {
+        return fixed;
+    }
+
+    /**
+     * Potentially declare the current property value as being "fixed."
+     * If <code>true</code>, the context will reuse any not-<code>UNKNOWN</code>
+     * propertyValue regardless of the {@link AccessStrategy} by which it is requested.
+     * @param fixed
+     */
+    public void setFixed(boolean fixed) {
+        this.fixed = fixed;
+    }
+
+    /**
+     * Depending on whether we have a metaProperty or not,
+     * this returns the metaProperty or otherwise the metaBean.
+     * This is used to have a simple way to request features
+     * in the Validation for both bean- and property-level validations.
+     *
+     * @return something that is capable to deliver features
+     */
+    public FeaturesCapable getMeta() {
+        return (metaProperty == null) ? metaBean : metaProperty;
+    }
+
+    /**
+     * Drop cached value, marking the internal cachedValue as <code>UNKNOWN</code>.
+     * This forces the BeanValidationContext to recompute the value
+     * the next time it is accessed.
+     * Use this method inside tests or when the propertyValue has been
+     * changed during validation.
+     */
+    public void unknownValue() {
+        propertyValue = UNKNOWN;
+        access = null;
+        fixed = false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MetaBean getMetaBean() {
+        return metaBean;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object getBean() {
+        return bean;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MetaProperty getMetaProperty() {
+        return metaProperty;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setMetaBean(MetaBean metaBean) {
+        this.metaBean = metaBean;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setBean(Object bean) {
+        this.bean = bean;
+        unknownValue();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setMetaProperty(MetaProperty metaProperty) {
+        this.metaProperty = metaProperty;
+        unknownValue();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return "BeanValidationContext{ bean=" + bean + ", metaProperty=" + metaProperty +
+              ", propertyValue=" + propertyValue + '}';
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void moveDown(MetaProperty prop, AccessStrategy access) {
+        setBean(getPropertyValue(access), prop.getMetaBean());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void moveUp(Object bean, MetaBean aMetaBean) {
+        setBean(bean, aMetaBean); // reset context state
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setCurrentIndex(Integer index) {
+        // do nothing
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setCurrentKey(Object key) {
+        // do nothing
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public AccessStrategy getAccess() {
+        return this.access;
+    }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/BeanValidator.java b/trunk/bval-core/src/main/java/org/apache/bval/BeanValidator.java
new file mode 100644
index 0000000..175e657
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/BeanValidator.java
@@ -0,0 +1,280 @@
+/*
+ *  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.bval;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Collection;
+
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.PropertyAccess;
+import org.apache.bval.util.ValidationHelper;
+
+// TODO: centralize treatMapsLikeBeans
+
+/**
+ * Description: Top-Level API-class to validate objects or object-trees. You can
+ * invoke, extend or utilize this class if you need other ways to integrate
+ * validation in your application.
+ * <p/>
+ * This class supports cyclic object graphs by keeping track of validated
+ * instances in the validation context.<br/>
+ */
+public class BeanValidator<T extends ValidationListener> {
+    private final MetaBeanFinder metaBeanFinder;
+
+    /**
+     * Create a new BeanValidator instance. Convenience constructor. Use the
+     * global instance of MetaBeanManagerFactory.getFinder().
+     */
+    public BeanValidator() {
+        this(MetaBeanManagerFactory.getFinder());
+    }
+
+    /**
+     * Create a new BeanValidator instance.
+     * 
+     * @param metaBeanFinder
+     */
+    public BeanValidator(MetaBeanFinder metaBeanFinder) {
+        this.metaBeanFinder = metaBeanFinder;
+    }
+
+    /**
+     * Convenience API. validate a root object with all related objects with its
+     * default metaBean definition.
+     * 
+     * @param bean
+     * @return results - validation results found
+     */
+    public T validate(Object bean) {
+        MetaBean metaBean = getMetaBeanFinder().findForClass(bean.getClass());
+        return validate(bean, metaBean);
+    }
+
+    /**
+     * Convenience API. validate a root object with all related objects
+     * according to the metaBean.
+     * 
+     * @param bean
+     *            - a single bean or a collection of beans (that share the same
+     *            metaBean!)
+     * @param metaBean
+     * @return results - validation results found
+     */
+    public T validate(Object bean, MetaBean metaBean) {
+        ValidationContext<T> context = createContext();
+        context.setBean(bean, metaBean);
+        ValidationHelper.validateContext(context, new BeanValidatorCallback(context), treatMapsLikeBeans);
+        return context.getListener();
+    }
+
+    /**
+     * Validate the method parameters based on @Validate annotations.
+     * Requirements: Parameter, that are to be validated must be annotated with @Validate
+     * 
+     * @param method
+     *            - a method
+     * @param parameters
+     *            - the parameters suitable to the method
+     * @return a validation result or null when there was nothing to validate
+     * @see Validate
+     */
+    public T validateCall(Method method, Object[] parameters) {
+        if (parameters.length > 0) {
+            // shortcut (for performance!)
+            Annotation[][] annotations = method.getParameterAnnotations();
+            ValidationContext<T> context = null;
+            for (int i = 0; i < parameters.length; i++) {
+                for (Annotation anno : annotations[i]) {
+                    if (anno instanceof Validate) {
+                        if (context == null)
+                            context = createContext();
+                        if (determineMetaBean((Validate) anno, parameters[i], context)) {
+                            ValidationHelper.validateContext(context, new BeanValidatorCallback(context),
+                                treatMapsLikeBeans);
+                            break; // next parameter
+                        }
+                    }
+                }
+            }
+            return context != null ? context.getListener() : null;
+        }
+        return null;
+    }
+
+    /**
+     * Determine the metabean for the given object.
+     * 
+     * @param <VL>
+     * @param validate
+     * @param parameter
+     * @param context
+     * @return true when validation should happen, false to skip it
+     */
+    protected <VL extends ValidationListener> boolean determineMetaBean(Validate validate, Object parameter,
+        ValidationContext<VL> context) {
+        if (validate.value().length() == 0) {
+            if (parameter == null)
+                return false;
+            Class<?> beanClass;
+            if (parameter instanceof Collection<?>) { // do not validate empty
+                                                      // collection
+                Collection<?> coll = ((Collection<?>) parameter);
+                if (coll.isEmpty())
+                    return false;
+                beanClass = coll.iterator().next().getClass(); // get first
+                                                               // object
+            } else if (parameter.getClass().isArray()) {
+                beanClass = parameter.getClass().getComponentType();
+            } else {
+                beanClass = parameter.getClass();
+            }
+            context.setBean(parameter, getMetaBeanFinder().findForClass(beanClass));
+        } else {
+            context.setBean(parameter, getMetaBeanFinder().findForId(validate.value()));
+        }
+        return true;
+    }
+
+    /**
+     * factory method - overwrite in subclasses
+     * 
+     * @return ValidationListener of the proper type
+     */
+    @SuppressWarnings("unchecked")
+    protected T createResults() {
+        return (T) new ValidationResults();
+    }
+
+    /**
+     * factory method - overwrite in subclasses
+     * 
+     * @return ValidationContext parameterized with our listener type
+     */
+    protected ValidationContext<T> createContext() {
+        return new BeanValidationContext<T>(createResults());
+    }
+
+    /**
+     * Convenience API. validate a single property.
+     * 
+     * @param bean
+     *            - the root object
+     * @param metaProperty
+     *            - metadata for the property
+     * @return validation results
+     */
+    public T validateProperty(Object bean, MetaProperty metaProperty) {
+        ValidationContext<T> context = createContext();
+        context.setBean(bean);
+        context.setMetaProperty(metaProperty);
+        ValidationHelper.validateProperty(context);
+        return context.getListener();
+    }
+
+    /**
+     * {@inheritDoc} internal validate a bean (=not a collection of beans) and
+     * its related beans
+     */
+    protected <VL extends ValidationListener> void validateBeanNet(ValidationContext<VL> context) {
+        if (context.collectValidated()) {
+            ValidationHelper.validateBean(context);
+            for (MetaProperty prop : context.getMetaBean().getProperties()) {
+                validateRelatedBean(context, prop);
+            }
+        }
+    }
+
+    /**
+     * Validate a property of a graph.
+     * 
+     * @param <VL>
+     * @param context
+     * @param prop
+     */
+    protected <VL extends ValidationListener> void validateRelatedBean(ValidationContext<VL> context, MetaProperty prop) {
+        AccessStrategy[] access = prop.getFeature(Features.Property.REF_CASCADE);
+        if (access == null && prop.getMetaBean() != null) { // single property
+                                                            // access strategy
+            // save old values from context
+            final Object bean = context.getBean();
+            final MetaBean mbean = context.getMetaBean();
+            // modify context state for relationship-target bean
+            context.moveDown(prop, new PropertyAccess(bean.getClass(), prop.getName()));
+            ValidationHelper.validateContext(context, new BeanValidatorCallback(context), treatMapsLikeBeans);
+            // restore old values in context
+            context.moveUp(bean, mbean);
+        } else if (access != null) { // different accesses to relation
+            // save old values from context
+            final Object bean = context.getBean();
+            final MetaBean mbean = context.getMetaBean();
+            for (AccessStrategy each : access) {
+                // modify context state for relationship-target bean
+                context.moveDown(prop, each);
+                ValidationHelper.validateContext(context, new BeanValidatorCallback(context), treatMapsLikeBeans);
+                // restore old values in context
+                context.moveUp(bean, mbean);
+            }
+        }
+    }
+
+    private boolean treatMapsLikeBeans = false;
+
+    public boolean isTreatMapsLikeBeans() {
+        return treatMapsLikeBeans;
+    }
+
+    public void setTreatMapsLikeBeans(boolean treatMapsLikeBeans) {
+        this.treatMapsLikeBeans = treatMapsLikeBeans;
+    }
+
+    /**
+     * Get the metabean finder associated with this validator.
+     * 
+     * @return a MetaBeanFinder
+     * @see org.apache.bval.MetaBeanManagerFactory#getFinder()
+     */
+    public MetaBeanFinder getMetaBeanFinder() {
+        return metaBeanFinder;
+    }
+
+    /**
+     * Dispatches a call from {@link #validate()} to
+     * {@link BeanValidator#validateBeanNet(ValidationContext)} with the current
+     * context set.
+     */
+    private class BeanValidatorCallback implements ValidationHelper.ValidateCallback {
+
+        private final ValidationContext<?> context;
+
+        public BeanValidatorCallback(ValidationContext<?> context) {
+            this.context = context;
+        }
+
+        public void validate() {
+            validateBeanNet(context);
+        }
+
+    }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/DynamicMetaBean.java b/trunk/bval-core/src/main/java/org/apache/bval/DynamicMetaBean.java
new file mode 100644
index 0000000..c5e22df
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/DynamicMetaBean.java
@@ -0,0 +1,49 @@
+/*
+ *  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.bval;
+
+import org.apache.bval.model.MetaBean;
+
+// TODO: Reduce visibility
+
+/**
+ * Description: Dynamic {@link MetaBean} subclass.<br/>
+ */
+public final class DynamicMetaBean extends MetaBean {
+    private static final long serialVersionUID = 1L;
+
+    private final MetaBeanFinder finder;
+
+    /**
+     * Create a new DynamicMetaBean instance.
+     * @param finder
+     */
+    public DynamicMetaBean(MetaBeanFinder finder) {
+        this.finder = finder;
+    }
+
+    /**
+     * {@inheritDoc}
+     * different strategies with hints to find MetaBean of associated object can
+     * be implemented here.
+     */
+    @Override
+    public MetaBean resolveMetaBean(Object bean) {
+        return bean instanceof Class<?> ?
+                finder.findForClass((Class<?>) bean) : finder.findForClass(bean.getClass());
+    }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/IntrospectorMetaBeanFactory.java b/trunk/bval-core/src/main/java/org/apache/bval/IntrospectorMetaBeanFactory.java
new file mode 100644
index 0000000..d0c9e48
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/IntrospectorMetaBeanFactory.java
@@ -0,0 +1,109 @@
+/*
+ *  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.bval;
+
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+
+import java.beans.BeanInfo;
+import java.beans.IndexedPropertyDescriptor;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Enumeration;
+
+import static org.apache.bval.model.Features.Property.*;
+
+/**
+ * Description: use information from java.beans.Introspector in MetaBeans. The PropertyDescriptor can contain info about
+ * HIDDEN, PREFERRED, READONLY and other features<br/>
+ * NOTE: THIS IS AN OPTIONAL CLASS, TO ENABLE IT, SET Factory Property apache.bval.enable-introspector="true"
+ */
+public class IntrospectorMetaBeanFactory implements MetaBeanFactory {
+
+    /**
+     * {@inheritDoc}
+     */
+    public void buildMetaBean(MetaBean meta) throws Exception {
+        if (meta.getBeanClass() == null) {
+            return; // handle only, when local class exists
+        }
+        BeanInfo info = Introspector.getBeanInfo(meta.getBeanClass());
+        if (meta.getName() == null && info.getBeanDescriptor() != null) {
+            meta.setName(info.getBeanDescriptor().getName()); // (display?)name = simple class name!
+        }
+        for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
+            if (!(pd instanceof IndexedPropertyDescriptor || pd.getName().equals("class"))) {
+                MetaProperty metaProp = buildMetaProperty(pd, meta.getProperty(pd.getName()));
+                meta.putProperty(pd.getName(), metaProp);
+            }
+        }
+    }
+
+    /**
+     * Create a {@link MetaProperty} from the specified {@link PropertyDescriptor}.
+     * 
+     * @param pd
+     * @return MetaProperty
+     */
+    @Deprecated
+    protected MetaProperty buildMetaProperty(PropertyDescriptor pd) {
+        return buildMetaProperty(pd, null);
+    }
+
+    /**
+     * Create a {@link MetaProperty} from the specified {@link PropertyDescriptor}.
+     * 
+     * @param pd
+     * @param existing
+     * @return MetaProperty
+     */
+    protected MetaProperty buildMetaProperty(PropertyDescriptor pd, MetaProperty existing) {
+        MetaProperty meta = new MetaProperty();
+        meta.setName(pd.getName());
+        meta.setType(determineGenericPropertyType(pd));
+        if (pd.isHidden()) {
+            meta.putFeature(HIDDEN, Boolean.TRUE);
+        }
+        if (pd.isPreferred()) {
+            meta.putFeature(PREFERRED, Boolean.TRUE);
+        }
+        if (pd.isConstrained()) {
+            meta.putFeature(READONLY, Boolean.TRUE);
+        }
+        Enumeration<String> enumeration = pd.attributeNames();
+        while (enumeration.hasMoreElements()) {
+            String key = enumeration.nextElement();
+            Object value = pd.getValue(key);
+            meta.putFeature(key, value);
+        }
+        return meta;
+    }
+
+    private Type determineGenericPropertyType(PropertyDescriptor pd) {
+        Method m = pd.getReadMethod();
+        if (m != null) {
+            return m.getGenericReturnType();
+        }
+        m = pd.getWriteMethod();
+        if (m != null && m.getParameterTypes().length == 1) {
+            return m.getGenericParameterTypes()[0];
+        }
+        return pd.getPropertyType();
+    }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanBuilder.java b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanBuilder.java
new file mode 100644
index 0000000..745b0fa
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanBuilder.java
@@ -0,0 +1,136 @@
+/*
+ *  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.bval;
+
+import org.apache.bval.model.MetaBean;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.ClassUtils;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Description: internal implementation class to construct metabeans with
+ * factories<br/>
+ */
+public class MetaBeanBuilder {
+
+    private static final Logger log =  Logger.getLogger(MetaBeanBuilder.class.getName());
+
+    /**
+     * here you can install different kinds of factories to create MetaBeans
+     * from
+     */
+    private MetaBeanFactory[] factories;
+
+    /**
+     * Create a new MetaBeanBuilder instance.
+     */
+    public MetaBeanBuilder() {
+        this(new MetaBeanFactory[] { new IntrospectorMetaBeanFactory() });
+    }
+
+    /**
+     * Create a new MetaBeanBuilder instance.
+     * 
+     * @param factories
+     */
+    public MetaBeanBuilder(MetaBeanFactory[] factories) {
+        setFactories(factories);
+    }
+
+    /**
+     * Get the configured set of {@link MetaBeanFactory} objects.
+     * 
+     * @return {@link MetaBeanFactory} array
+     */
+    public MetaBeanFactory[] getFactories() {
+        return ArrayUtils.clone(factories);
+    }
+
+    /**
+     * Set the array of {@link MetaBeanFactory} instances with which to enrich
+     * {@link MetaBean}s.
+     * 
+     * @param factories
+     */
+    public void setFactories(MetaBeanFactory[] factories) {
+        this.factories = ArrayUtils.clone(factories);
+    }
+
+    /**
+     * Build a {@link MetaBean} for a given id.
+     * 
+     * @param beanInfoId
+     * @return MetaBean
+     * @throws Exception
+     *             if unable to build
+     */
+    public MetaBean buildForId(String beanInfoId) throws Exception {
+        throw new IllegalArgumentException("MetaBean " + beanInfoId + " not found");
+    }
+
+    /**
+     * Build beans for all known ids. Default implementation returns an empty
+     * map.
+     * 
+     * @return Map of String : MetaBean
+     */
+    public Map<String, MetaBean> buildAll() throws Exception {
+        return new HashMap<String, MetaBean>();
+    }
+
+    /**
+     * Find the named class.
+     * 
+     * @param className
+     * @return Class found or null
+     */
+    protected Class<?> findLocalClass(String className) {
+        if (className != null) {
+            try {
+                return ClassUtils.getClass(className);
+            } catch (ClassNotFoundException e) {
+            	log.log(Level.FINE, String.format("Class not found: %s", className), e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Build a MetaBean for the specified class.
+     * 
+     * @param clazz
+     * @return MetaBean
+     * @throws Exception
+     */
+    public MetaBean buildForClass(Class<?> clazz) throws Exception {
+        MetaBean meta = new MetaBean();
+        if (clazz != null) { // local class here?
+            meta.setBeanClass(clazz);
+            meta.setId(clazz.getName()); // default id = full class name!
+        }
+        for (MetaBeanFactory factory : factories) {
+            factory.buildMetaBean(meta);
+        }
+        return meta;
+    }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanCache.java b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanCache.java
new file mode 100644
index 0000000..c55bc0a
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanCache.java
@@ -0,0 +1,113 @@
+/*
+ *  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.bval;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.bval.model.MetaBean;
+
+/**
+ * Description: a cache to hold metabeans by id and by class.<br/>
+ */
+public class MetaBeanCache implements MetaBeanFinder, Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Cache keyed by id.
+     */
+    protected final ConcurrentMap<String, MetaBean> cacheById = new ConcurrentHashMap<String, MetaBean>();
+    /**
+     * Cache keyed by class.
+     */
+    protected final ConcurrentMap<Class<?>, MetaBean> cacheByClass = new ConcurrentHashMap<Class<?>, MetaBean>();
+
+    /**
+     * Create a new MetaBeanCache instance.
+     */
+    public MetaBeanCache() {
+        super();
+    }
+
+    /**
+     * Create a new MetaBeanCache instance.
+     * @param beans
+     */
+    public MetaBeanCache(Map<String, MetaBean> beans) {
+        this();
+        for (MetaBean bean : beans.values()) {
+            cache(bean);
+        }
+    }
+
+    /**
+     * Clear the cache.
+     */
+    public void clear() {
+        cacheById.clear();
+        cacheByClass.clear();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MetaBean findForId(String beanInfoId) {
+        return cacheById.get(beanInfoId);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MetaBean findForClass(Class<?> clazz) {
+        return cacheByClass.get(clazz);
+    }
+
+    /**
+     * Return all cached MetaBeans by id.
+     * @return live map
+     */
+    public Map<String, MetaBean> findAll() {
+        return cacheById;
+    }
+
+    /**
+     * Cache the specified MetaBean.
+     * @param beanInfo
+     */
+    public void cache(MetaBean beanInfo) {
+        cacheById.put(beanInfo.getId(), beanInfo);
+        if (beanInfo.getBeanClass() != null &&
+                beanInfo.getId().equals(beanInfo.getBeanClass().getName())) {
+            cacheByClass.putIfAbsent(beanInfo.getBeanClass(), beanInfo);
+        }
+    }
+
+    /**
+     * Remove a single MetaBean from the cache.
+     * @param beanInfo
+     */
+    public void removeFromCache(MetaBean beanInfo) {
+        cacheById.remove(beanInfo.getId());
+        if (beanInfo.getBeanClass() != null &&
+                beanInfo.getId().equals(beanInfo.getBeanClass().getName())) {
+            cacheByClass.remove(beanInfo.getBeanClass());
+        }
+    }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanFactory.java b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanFactory.java
new file mode 100644
index 0000000..e5a06d7
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanFactory.java
@@ -0,0 +1,32 @@
+/*
+ *  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.bval;
+
+import org.apache.bval.model.MetaBean;
+
+/**
+ * Description: interface for abstraction how to initialize a MetaBean
+ * with information from somewhere<br/>
+ */
+public interface MetaBeanFactory {
+    /**
+     * Initialize the specified {@link MetaBean}.
+     * @param metaBean
+     * @throws Exception
+     */
+    void buildMetaBean(MetaBean metaBean) throws Exception;
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanFinder.java b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanFinder.java
new file mode 100644
index 0000000..1caa97e
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanFinder.java
@@ -0,0 +1,41 @@
+/*
+ *  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.bval;
+
+
+import org.apache.bval.model.MetaBean;
+
+/**
+ * Description: Interface to find BeanInfos <br/>
+ */
+public interface MetaBeanFinder {
+    /**
+     * Find a MetaBean with a certain id.
+     * @param beanInfoId - symbolic unique name of Meta Info
+     * @return BeanInfo
+     * @throws IllegalArgumentException - when MetaBean not found
+     */
+    MetaBean findForId(String beanInfoId);
+
+    /**
+     * Find the MetaBean for the specified class.
+     * @param clazz - bean class
+     * @return BeanInfo (never null)
+     */
+    MetaBean findForClass(Class<?> clazz);
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanManager.java b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanManager.java
new file mode 100644
index 0000000..fe4797e
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanManager.java
@@ -0,0 +1,143 @@
+/*
+ *  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.bval;
+
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+
+import static org.apache.bval.model.Features.Property.*;
+
+/**
+ * Description: Default implementation for the interface to find, register and
+ * create MetaBeans. In most situations a single instance of this class is
+ * sufficient and you can get this instance from the
+ * {@link MetaBeanManagerFactory}. <br/>
+ */
+public class MetaBeanManager implements MetaBeanFinder {
+
+    /** MetaBean cache */
+    protected final MetaBeanCache cache = new MetaBeanCache();
+    /** MetaBean builder */
+    protected final MetaBeanBuilder builder;
+    /** Complete flag */
+    protected boolean complete = false;
+
+    /**
+     * Create a new MetaBeanManager instance.
+     */
+    public MetaBeanManager() {
+        builder = new MetaBeanBuilder();
+    }
+
+    /**
+     * Create a new MetaBeanManager instance.
+     * 
+     * @param builder
+     */
+    public MetaBeanManager(MetaBeanBuilder builder) {
+        this.builder = builder;
+    }
+
+    /**
+     * Get the builder used.
+     * 
+     * @return {@link MetaBeanBuilder}
+     */
+    public MetaBeanBuilder getBuilder() {
+        return builder;
+    }
+
+    /**
+     * Get the cache used.
+     * 
+     * @return {@link MetaBeanCache}
+     */
+    public MetaBeanCache getCache() {
+        return cache;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MetaBean findForId(String beanInfoId) {
+        MetaBean beanInfo = cache.findForId(beanInfoId);
+        if (beanInfo != null)
+            return beanInfo;
+        try {
+            beanInfo = builder.buildForId(beanInfoId);
+            cache.cache(beanInfo);
+            computeRelationships(beanInfo);
+            return beanInfo;
+        } catch (RuntimeException e) {
+            throw e; // do not wrap runtime exceptions
+        } catch (Exception e) {
+            throw new IllegalArgumentException("error creating beanInfo with id: " + beanInfoId, e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MetaBean findForClass(Class<?> clazz) {
+        if (clazz == null)
+            return null;
+        MetaBean beanInfo = cache.findForClass(clazz);
+        if (beanInfo != null)
+            return beanInfo;
+        try {
+            beanInfo = builder.buildForClass(clazz);
+            cache.cache(beanInfo);
+            computeRelationships(beanInfo);
+            return beanInfo;
+        } catch (RuntimeException e) {
+            throw e; // do not wrap runtime exceptions
+        } catch (Exception e) {
+            throw new IllegalArgumentException("error creating beanInfo for " + clazz, e);
+        }
+    }
+
+    /**
+     * Compute all known relationships for <code>beanInfo</code>. must be called
+     * AFTER cache.cache() to avoid endless loop
+     * 
+     * @param beanInfo
+     *            - the bean for which to compute relationships
+     */
+    protected void computeRelationships(MetaBean beanInfo) {
+        for (MetaProperty prop : beanInfo.getProperties()) {
+            String beanRef = (String) prop.getFeature(REF_BEAN_ID);
+            computeRelatedMetaBean(prop, beanRef);
+        }
+    }
+
+    /**
+     * Compute a single related {@link MetaBean}.
+     * 
+     * @param prop
+     * @param beanRef
+     */
+    protected void computeRelatedMetaBean(MetaProperty prop, String beanRef) {
+        Class<?> beanType = prop.getFeature(REF_BEAN_TYPE);
+        if (beanType != null) {
+            prop.setMetaBean(findForClass(beanType));
+        } else if (prop.getFeature(REF_CASCADE) != null) { // dynamic type
+                                                           // resolution:
+            prop.setMetaBean(new DynamicMetaBean(this));
+        }
+    }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanManagerFactory.java b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanManagerFactory.java
new file mode 100644
index 0000000..f2cd8da
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/MetaBeanManagerFactory.java
@@ -0,0 +1,46 @@
+/*
+ *  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.bval;
+
+/**
+ * Description: API class to hold a singleton of a {@link MetaBeanManager}
+ * that implements the finder and registry interfaces for MetaBeans<br/>
+ *
+ * @see org.apache.bval.model.MetaBean
+ * @see MetaBeanManager
+ */
+public class MetaBeanManagerFactory {
+    private static MetaBeanManager manager = new MetaBeanManager();
+
+    /**
+     * global meta bean finder.
+     * @return the singleton
+     */
+    public static MetaBeanFinder getFinder() {
+        return manager;
+    }
+
+
+    /**
+     * set global meta bean manager, that is responsible
+     * for finding, caching, xml registry and enrichment algorithm.
+     * @param finder
+     */
+    public static void setManager(MetaBeanManager finder) {
+        manager = finder;
+    }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/Validate.java b/trunk/bval-core/src/main/java/org/apache/bval/Validate.java
new file mode 100644
index 0000000..039106d
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/Validate.java
@@ -0,0 +1,41 @@
+/*
+ *  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.bval;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Description: Annotate an element (parameter) to be validated.
+ * <br>
+ */
+@Target({METHOD, FIELD, CONSTRUCTOR, PARAMETER})
+@Retention(RUNTIME)
+public @interface Validate {
+    /**
+     * (optional) the MetaBean.id to use
+     */
+    String value() default "";
+
+    /**
+     * to Support groups on @Valid(ate) in method/return validation
+     */
+    Class<?>[] groups() default {};
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/ValidationResults.java b/trunk/bval-core/src/main/java/org/apache/bval/ValidationResults.java
new file mode 100644
index 0000000..e9d79f0
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/ValidationResults.java
@@ -0,0 +1,211 @@
+/*
+ *  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.bval;
+
+
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * Description: Implements a contains to hold and transport validation results<br/>
+ */
+public class ValidationResults implements ValidationListener, Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private Map<String, List<Error>> errorsByReason;
+    private Map<Object, Map<String, List<Error>>> errorsByOwner;
+
+
+    /**
+     * API to add an error to the validation results.
+     *
+     * @param reason       - Features from {@link org.apache.bval.model.Features.Property}
+     *                       or custom reason of validation error
+     * @param context        - context information (bean, propertyName, value, ...)
+     */
+    public <T extends ValidationListener> void addError(String reason, ValidationContext<T> context) {
+        Error error = createError(reason, context.getBean(), context.getPropertyName());
+        addError(error, context);
+    }
+
+
+     /**
+     * API to add an error to the validation results.
+     *
+     * @param error       - holding the description of reason and object to describe
+      *                     the validation error
+     * @param context     - null or the context to provide additional information
+     */
+    public <T extends ValidationListener> void addError(Error error, ValidationContext<T> context) {
+        if (errorsByReason == null) {
+            initialize();
+        }
+        addToReasonBucket(error);
+        addToOwnerBucket(error);
+    }
+
+    /**
+     * Old API to add an error to the validation results when no context is available.
+     *
+     * @param reason       - Features from {@link org.apache.bval.model.Features.Property} or custom validation reason
+     * @param bean         - (optional) owner bean or null
+     * @param propertyName - (optional) propertyName where valiation error occurred or null
+     */
+    public void addError(String reason, Object bean, String propertyName) {
+        addError(createError(reason, bean, propertyName), null);
+    }
+
+    /**
+     * Create an Error object.
+     * @param reason
+     * @param owner
+     * @param propertyName
+     * @return new {@link Error}
+     */
+    protected Error createError(String reason, Object owner, String propertyName) {
+        return new Error(reason, owner, propertyName);
+    }
+
+    /**
+     * initialize the error-buckets now when needed and
+     * not on instance creation to save memory garbage.
+     */
+    protected void initialize() {
+        errorsByReason = new LinkedHashMap<String, List<Error>>();
+        errorsByOwner = new LinkedHashMap<Object, Map<String, List<Error>>>();
+    }
+
+    /**
+     * Add an Error to the set of Errors shared by a particular "reason."
+     * @param error
+     * @see {@link Error#getReason()}
+     */
+    protected void addToReasonBucket(Error error) {
+        if (error.getReason() == null) return;
+
+        List<Error> errors = errorsByReason.get(error.getReason());
+        if (errors == null) {
+            errors = new ArrayList<Error>();
+            errorsByReason.put(error.getReason(), errors);
+        }
+        errors.add(error);
+    }
+
+    /**
+     * Add an Error to the property-keyed map of Errors maintained for this Error's owner.
+     * @param error
+     * @see {@link Error#getOwner()}
+     */
+    protected void addToOwnerBucket(Error error) {
+        if (error.getOwner() == null) return;
+
+        Map<String, List<Error>> errors = errorsByOwner.get(error.getOwner());
+        if (errors == null) {
+            errors = new HashMap<String, List<Error>>();
+            errorsByOwner.put(error.getOwner(), errors);
+        }
+        List<Error> list = errors.get(error.getPropertyName());
+        if (list == null) {
+            list = new ArrayList<Error>();
+            errors.put(error.getPropertyName(), list);
+        }
+        list.add(error);
+    }
+
+    /**
+     * Get the map of Errors by reason; 
+     * key = reason, value = list of errors for this reason
+     * @return map
+     */
+    public Map<String, List<Error>> getErrorsByReason() {
+        if (errorsByReason == null) return Collections.emptyMap();
+        return errorsByReason;
+    }
+
+    /**
+     * Get the map of Errors by owner;
+     * key = owner, value = map with:<br>
+     * &nbsp;&nbsp; key = propertyName, value = list of errors for this owner.propertyName
+     * @return map
+     */
+    public Map<Object, Map<String, List<Error>>> getErrorsByOwner() {
+        if (errorsByOwner == null) return Collections.emptyMap();
+        return errorsByOwner;
+    }
+
+    /**
+     * Learn whether these results are empty/error-free.
+     * @return true when there are NO errors in this validation result
+     */
+    public boolean isEmpty() {
+        if (errorsByReason == null ||
+              (errorsByReason.isEmpty() && errorsByOwner.isEmpty())) return true;
+        for (List<Error> list : errorsByReason.values()) {
+            if (!list.isEmpty()) return false;
+        }
+        for (Map<String, List<Error>> map : errorsByOwner.values()) {
+            for (List<Error> list : map.values()) {
+                if (!list.isEmpty()) return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Learn whether there is an Error keyed to a specified reason description.
+     * @param reason
+     * @return boolean
+     * @see {@link Error#getReason()}
+     */
+    public boolean hasErrorForReason(String reason) {
+        if (errorsByReason == null) return false;
+        List<Error> errors = errorsByReason.get(reason);
+        return errors != null && !errors.isEmpty();
+    }
+
+    /**
+     * Learn whether <code>bean</code> has any errors keyed to property <code>propertyName</code>.
+     * @param bean
+     * @param propertyName - may be null: any property is checked
+     *                     OR the name of the property to check
+     * @return boolean
+     */
+    public boolean hasError(Object bean, String propertyName) {
+        if (errorsByOwner == null) return false;
+        Map<String, List<Error>> errors = errorsByOwner.get(bean);
+        if (errors == null) return false;
+        if (propertyName != null) {
+            List<Error> list = errors.get(propertyName);
+            return list != null && !list.isEmpty();
+        } else {
+            for (List<Error> list : errors.values()) {
+                if (!list.isEmpty()) return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return "ValidationResults{" + errorsByOwner + "}";
+    }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/DynaType.java b/trunk/bval-core/src/main/java/org/apache/bval/model/DynaType.java
new file mode 100644
index 0000000..7a89862
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/DynaType.java
@@ -0,0 +1,31 @@
+/*
+ *  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.bval.model;
+
+import java.lang.reflect.Type;
+
+/**
+ * Description: implementation of a dynamic type. can be used inside a
+ * MetaProperty for instance-based types <br/>
+ */
+public interface DynaType extends Type {
+    /**
+     * Resolve the type indirection.
+     * @return Type
+     */
+    Type getRawType();
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/DynaTypeEnum.java b/trunk/bval-core/src/main/java/org/apache/bval/model/DynaTypeEnum.java
new file mode 100644
index 0000000..d520807
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/DynaTypeEnum.java
@@ -0,0 +1,122 @@
+/*
+ *  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.bval.model;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * Description: ("artificial" enum with custom values).<br/>
+ */
+public class DynaTypeEnum implements DynaType {
+    private final Class<?> enumClass;
+    private Value[] enumConstants;
+
+    /**
+     * Create a new DynaTypeEnum instance.
+     * @param enumClass
+     */
+    public DynaTypeEnum(Class<?> enumClass) {
+        this.enumClass = enumClass;
+    }
+
+    /**
+     * Create a new DynaTypeEnum instance.
+     * @param enumClass
+     * @param names
+     */
+    public DynaTypeEnum(Class<?> enumClass, String... names) {
+        this(enumClass);
+        setEnumNames(names);
+    }
+
+    /**
+     * Set the enumeration value names.
+     * @param names
+     */
+    public void setEnumNames(String[] names) {
+        enumConstants = new Value[names.length];
+        int i = 0;
+        for (String each : names) {
+            enumConstants[i++] = new Value(each);
+        }
+    }
+
+    /**
+     * Get the name of the enum class.
+     * @return String
+     */
+    public String getName() {
+        return enumClass.getName();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Class<?> getRawType() {
+        return enumClass;
+    }
+
+    /**
+     * Learn whether the referred class is, in fact, an enum class.
+     * used by freemarker-template "bean-infos-json.ftl"
+     */
+    public boolean isEnum() {
+        return enumClass.isEnum();
+    }
+
+    /**
+     * Get the emulated constants.
+     * used by freemarker-template "bean-infos-json.ftl"
+     * @return Value[]
+     */
+    public Value[] getEnumConstants() {
+        return ArrayUtils.clone(enumConstants);
+    }
+
+    /**
+     * Learn whether the wrapped class is assignable from <code>cls</code>.
+     * @param cls
+     * @return boolean
+     */
+    public boolean isAssignableFrom(Class<?> cls) {
+        return enumClass.isAssignableFrom(cls);
+    }
+
+    /**
+     * Represents a single "enum" instance (= the value).
+     */
+    public static final class Value {
+        final String name;
+
+        /**
+         * Create a new Value instance.
+         * @param name
+         */
+        Value(String name) {
+            this.name = name;
+        }
+
+        /**
+         * used by freemarker-template "bean-infos-json.ftl"
+         * @return the name of this constant
+         */
+        public String name() {
+            return name;
+        }
+
+    }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/Features.java b/trunk/bval-core/src/main/java/org/apache/bval/model/Features.java
new file mode 100644
index 0000000..d362efa
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/Features.java
@@ -0,0 +1,114 @@
+/*
+ *  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.bval.model;
+
+/**
+ * Description: Contains key of common feature keys used by standard validators etc.
+ * This DOES NOT MEAN that the list of property- or bean-features is closed. You can
+ * put anything into the metabean as a feature and use it in your custom validators
+ * and other classes that access your metabeans.<br/>
+ *
+ * @see FeaturesCapable
+ */
+public interface Features {
+    /** Features of {@link MetaBean} */
+    public interface Bean {
+        /** INFO: String, name of the Property, that is the Primary Key */
+        String MAIN_KEY = "mainKey";
+        /** INFO: category/domain to which the metaBean belongs to */
+        String DOMAIN = "domain";
+
+        //        String DISPLAY_NAME = "displayName";
+        String UNIQUE_KEY = "uniqueKey";
+    }
+
+    /** Features of {@link MetaProperty} */
+    public interface Property {
+        /** INFO: possible Enum values */
+        String ENUM = "enum";
+        /** INFO: Boolean, TRUE if Property is a Unique Key */
+        String UNIQUE_KEY = "uniqueKey";
+        /** VALIDATION: Boolean, mandatory field? */
+        String MANDATORY = "mandatory";
+        /** VALIDATION: Integer, max. number of chars/digits / max. cardinality of a to-many relationship */
+        String MAX_LENGTH = "maxLen";
+        /** VALIDATION: Comparable (e.g. a subclass of Number), max value */
+        String MAX_VALUE = "maxValue";
+        /** VALIDATION: Integer, min. number of chars/digits / min. cardinality of a to-many relationship */
+        String MIN_LENGTH = "minLen";
+        /** VALIDATION: Comparable (e.g. a subclass of Number), min value */
+        String MIN_VALUE = "minValue";
+        /** INFO: String-representation of a default value */
+        String DEFAULT_VALUE = "defValue";
+        /** SECURITY, INFO: Boolean, is value or relationship unmodifiable */
+        String READONLY = "readonly";
+        /**
+         * SECURITY, INFO: Boolean, Field accessible?
+         * If false, the field must not be displayed, queried, changed.
+         */
+        String DENIED = "denied";
+        /** VALIDATION: String, regular expression to validate the format of input data */
+        String REG_EXP = "regExp";
+        /**
+         * VALIDATION: String, Constraint for time-information of a Date-field:
+         * {@link org.apache.bval.xml.XMLMetaValue#TIMELAG_Past}
+         * or
+         * {@link org.apache.bval.xml.XMLMetaValue#TIMELAG_Future}
+         */
+        String TIME_LAG = "timeLag";
+
+        /**
+         * INFO: Boolean, Field visible?
+         *
+         * @see java.beans.PropertyDescriptor#isHidden()
+         */
+        String HIDDEN = "hidden";
+        /**
+         * INFO: Boolean
+         *
+         * @see java.beans.PropertyDescriptor#isPreferred()
+         */
+        String PREFERRED = "preferred";
+
+        /** INFO: relationship's target metaBean.id * */
+        String REF_BEAN_ID = "refBeanId";
+
+        /**
+         * INFO: Class<br>
+         * Relationship's target metaBean.beanClass.
+         * In case of to-many relationships, this feature
+         * hold the Bean-type not the Collection-type.
+         */
+        String REF_BEAN_TYPE = "refBeanType";
+
+        /**
+         * INFO: AccessStrategy[]<br>
+         * an array of accessStrategies
+         * how validation should cascade into relationship target beans<br>
+         * null when validation should NOT cascade into relationship target
+         * beans<br>
+         * <p/>
+         * Default: {PropertyAccess(metaProperty.name)},
+         * when MetaProperty.metaBean is != null
+         */
+        String REF_CASCADE = "refCascade";
+
+        /** INFO: an array with the string names of custom java script validation functions */
+        @Deprecated // TODO RSt - I suggest to remove this and all related code
+        String JAVASCRIPT_VALIDATION_FUNCTIONS = "jsFunctions";
+    }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/FeaturesCapable.java b/trunk/bval-core/src/main/java/org/apache/bval/model/FeaturesCapable.java
new file mode 100644
index 0000000..d495dc1
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/FeaturesCapable.java
@@ -0,0 +1,219 @@
+/*
+ *  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.bval.model;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * Description: abstract superclass of meta objects that support a map of
+ * features.<br/>
+ */
+public abstract class FeaturesCapable implements Serializable {
+    private static final long serialVersionUID = -4045110242904814218L;
+
+    private ConcurrentMap<String, Object> features = createFeaturesMap();
+
+    /** key = validation id, value = the validation */
+    private Validation[] validations = new Validation[0];
+
+    private volatile boolean locking;
+    private ReentrantLock lock = new ReentrantLock(true);
+
+    /**
+     * Create a new FeaturesCapable instance.
+     */
+    public FeaturesCapable() {
+        super();
+    }
+
+    /**
+     * Get the (live) map of features.
+     * 
+     * @return Map<String, Object>
+     */
+    public Map<String, Object> getFeatures() {
+        return features;
+    }
+
+    /**
+     * Set whether to optimize read operations by accessing the features map in
+     * an unsynchronized manner.
+     * 
+     * @param fast
+     */
+    public void optimizeRead(boolean fast) {
+        lock.lock();
+        try {
+            this.locking = !fast;
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    /**
+     * Get the specified feature.
+     * 
+     * @param <T>
+     * @param key
+     * @return T
+     */
+    public <T> T getFeature(String key) {
+        return getFeature(key, (T) null);
+    }
+
+    /**
+     * Get the specified feature, returning <code>defaultValue</code> if
+     * undeclared.
+     * 
+     * @param <T>
+     * @param key
+     * @param defaultValue
+     * @return T
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T getFeature(String key, T defaultValue) {
+        boolean release = acquireLockIfNeeded();
+        try {
+            return features.containsKey(key) ? (T) features.get(key) : defaultValue;
+        } finally {
+            if (release) {
+                lock.unlock();
+            }
+        }
+    }
+
+    /**
+     * Convenience method to set a particular feature value.
+     * 
+     * @param <T>
+     * @param key
+     * @param value
+     */
+    public <T> void putFeature(String key, T value) {
+        boolean release = acquireLockIfNeeded();
+        try {
+            features.put(key, value);
+        } finally {
+            if (release) {
+                lock.unlock();
+            }
+        }
+    }
+
+    /**
+     * Create a deep copy (copy receiver and copy properties).
+     * 
+     * @param <T>
+     * @return new T instance
+     */
+    public <T extends FeaturesCapable> T copy() {
+        try {
+            @SuppressWarnings("unchecked")
+            final T self = (T) clone();
+            copyInto(self);
+            return self;
+        } catch (CloneNotSupportedException e) {
+            throw new IllegalStateException("cannot clone() " + this, e);
+        }
+    }
+
+    /**
+     * Copy this {@link FeaturesCapable} into another {@link FeaturesCapable}
+     * instance.
+     * 
+     * @param <T>
+     * @param target
+     */
+    protected <T extends FeaturesCapable> void copyInto(T target) {
+        target.features = target.createFeaturesMap();
+        target.features.putAll(features);
+        if (validations != null) {
+            target.validations = validations.clone();
+        }
+    }
+
+    /**
+     * Get any validations set for this {@link FeaturesCapable}.
+     * 
+     * @return Validation array
+     */
+    public Validation[] getValidations() {
+        return ArrayUtils.clone(validations);
+    }
+
+    /**
+     * Set the validations for this {@link FeaturesCapable}.
+     * 
+     * @param validations
+     */
+    public void setValidations(Validation[] validations) {
+        this.validations = ArrayUtils.clone(validations);
+    }
+
+    /**
+     * Add a validation to this {@link FeaturesCapable}.
+     * 
+     * @param validation
+     *            to add
+     */
+    public void addValidation(Validation validation) {
+        validations = ArrayUtils.add(validations, validation);
+    }
+
+    /**
+     * Search for an equivalent validation among those configured.
+     * 
+     * @param aValidation
+     * @return true if found
+     */
+    public boolean hasValidation(Validation aValidation) {
+        if (validations == null)
+            return false;
+        for (Validation validation : validations) {
+            if (validation.equals(aValidation))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Create a features map for this {@link FeaturesCapable} object.
+     * @return ConcurrentMap
+     */
+    protected ConcurrentMap<String, Object> createFeaturesMap() {
+        return new ConcurrentHashMap<String, Object>();
+    }
+
+    private boolean acquireLockIfNeeded() {
+        if (locking) {
+            lock.lock();
+            // double read
+            if (locking) {
+                return true;
+            }
+            lock.unlock();
+        }
+        return false;
+    }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/MetaBean.java b/trunk/bval-core/src/main/java/org/apache/bval/model/MetaBean.java
new file mode 100644
index 0000000..05ce598
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/MetaBean.java
@@ -0,0 +1,246 @@
+/*
+ *  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.bval.model;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * Description: the meta description of a bean or class. the class/bean itself can have a map of features and an array
+ * of metaproperties.<br/>
+ * 
+ * @see MetaProperty
+ */
+public class MetaBean extends FeaturesCapable implements Cloneable, Features.Bean {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Comparator for managing the sorted properties array.
+     */
+    private static class PropertyNameComparator implements Comparator<Object> {
+        /** Static instance */
+        static final PropertyNameComparator INSTANCE = new PropertyNameComparator();
+
+        /**
+         * {@inheritDoc}
+         */
+        public int compare(Object o1, Object o2) {
+            return getName(o1).compareTo(getName(o2));
+        }
+
+        private String getName(Object o) {
+            if (o == null) {
+                throw new NullPointerException();
+            }
+            return o instanceof MetaProperty ? ((MetaProperty) o).getName() : String.valueOf(o);
+        }
+    }
+
+    private String id;
+    private String name;
+    private Class<?> beanClass;
+    private MetaProperty[] properties = new MetaProperty[0];
+
+    /**
+     * Get the id.
+     * 
+     * @return String
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * Set the id.
+     * 
+     * @param id
+     *            the String to set
+     */
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
+     * Get the name.
+     * 
+     * @return String
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Set the name.
+     * 
+     * @param name
+     *            the String to set
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Get the beanClass.
+     * 
+     * @return Class<?>
+     */
+    public Class<?> getBeanClass() {
+        return beanClass;
+    }
+
+    /**
+     * Set the beanClass.
+     * 
+     * @param beanClass
+     *            the Class<?> to set
+     */
+    public void setBeanClass(Class<?> beanClass) {
+        this.beanClass = beanClass;
+    }
+
+    /**
+     * Get the properties.
+     * 
+     * @return MetaProperty[]
+     */
+    public MetaProperty[] getProperties() {
+        return ArrayUtils.clone(properties);
+    }
+
+    /**
+     * Set the properties.
+     * 
+     * @param properties
+     *            the MetaProperty[] to set
+     */
+    public void setProperties(MetaProperty[] properties) {
+        this.properties = ArrayUtils.clone(properties);
+        Arrays.sort(this.properties, PropertyNameComparator.INSTANCE);
+    }
+
+    /**
+     * Get the specified {@link MetaProperty}.
+     * 
+     * @param name
+     * @return MetaProperty found or <code>null</code>
+     */
+    public MetaProperty getProperty(String name) {
+        final MetaProperty[] props = properties;
+        int pos = Arrays.binarySearch(props, name, PropertyNameComparator.INSTANCE);
+        return pos < 0 ? null : props[pos];
+    }
+
+    /**
+     * Learn whether any known property is a relationship.
+     * 
+     * @see MetaProperty#isRelationship()
+     * @return true when at least one of the properties is a relationship
+     */
+    public boolean hasRelationships() {
+        for (MetaProperty p : properties) {
+            if (p.isRelationship()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Learn whether there are any known properties.
+     * 
+     * @return boolean
+     */
+    public boolean hasProperties() {
+        return properties.length > 0;
+    }
+
+    /**
+     * bidirectional - set the relationship between a MetaProperty and its parentMetaBean
+     * 
+     * @param name
+     * @param property
+     *            if <code>null</code>, remove
+     */
+    public void putProperty(String name, MetaProperty property) {
+        if (property != null) {
+            property.setParentMetaBean(this);
+        }
+        Object key = property == null ? name : property;
+        // make a local copy for consistency
+        MetaProperty[] props = properties;
+        int pos = Arrays.binarySearch(props, key, PropertyNameComparator.INSTANCE);
+        if (pos < 0) {
+            if (property == null) {
+                // store null property for unknown name == NOOP
+                return;
+            }
+            props = ArrayUtils.add(props, 0 - pos - 1, property);
+        } else {
+            if (property == null) {
+                props = ArrayUtils.remove(props, pos);
+            } else {
+                props[pos] = property;
+            }
+        }
+        this.properties = props;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return "MetaBean{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", beanClass=" + beanClass + '}';
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected <T extends FeaturesCapable> void copyInto(T target) {
+        super.copyInto(target);
+        final MetaBean copy = (MetaBean) target;
+        if (properties != null) {
+            copy.properties = properties.clone();
+            for (int i = copy.properties.length - 1; i >= 0; i--) {
+                copy.properties[i] = copy.properties[i].copy();
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * If this {@link MetaBean} is compatible with <code>bean</code>, return <code>this</code>, else <code>null</code>.
+     * </p>
+     * <p>
+     * Compatibility is satisfied in one of the following ways:
+     * <ul>
+     * <li><code>bean</code> is null</li>
+     * <li><code>bean</code> is an instance of our <code>beanClass</code></li>
+     * <li><code>bean</code> <em>is</em> our <code>beanClass</code> itself</li>
+     * </ul>
+     * </p>
+     * 
+     * @param bean
+     * @return <code>this</code> or <code>null</code>
+     */
+    public MetaBean resolveMetaBean(Object bean) {
+        return bean == null || bean == beanClass || beanClass.isInstance(bean) ? this : null;
+    }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/MetaProperty.java b/trunk/bval-core/src/main/java/org/apache/bval/model/MetaProperty.java
new file mode 100644
index 0000000..48ddf38
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/MetaProperty.java
@@ -0,0 +1,175 @@
+/*
+ *  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.bval.model;
+
+import java.lang.reflect.Type;
+
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+/**
+ * Description: the meta description of a property of a bean. It supports a map
+ * of features and multiple validations.<br/>
+ *
+ * @see Validation
+ * @see MetaBean
+ */
+public class MetaProperty extends FeaturesCapable
+      implements Cloneable, Features.Property {
+    private static final long serialVersionUID = 1L;
+
+    private String name;
+
+    private Type type;
+    private MetaBean metaBean;
+    private MetaBean parentMetaBean;
+
+    /**
+     * Create a new MetaProperty instance.
+     */
+    public MetaProperty() {
+    }
+
+    /**
+     * Get the metabean of the target bean (mainly for relationships).
+     * @return MetaBean (may be null).
+     */
+    public MetaBean getMetaBean() {
+        return metaBean;
+    }
+
+    /**
+     * Set the MetaBean of this {@link MetaProperty}.
+     * @param metaBean to set
+     */
+    public void setMetaBean(MetaBean metaBean) {
+        this.metaBean = metaBean;
+    }
+
+    /**
+     * Get the metabean that owns this property (set by MetaBean.putProperty())
+     * @return
+     */
+    public MetaBean getParentMetaBean() {
+        return parentMetaBean;
+    }
+
+    /**
+     * Set the metabean that owns this property (usually called by MetaBean.putProperty())
+     * @param parentMetaBean
+     */
+    void setParentMetaBean(MetaBean parentMetaBean) {
+        this.parentMetaBean = parentMetaBean;
+    }
+
+    /**
+     * Learn whether this property is considered a relationship.
+     * @return <code>true</code> if it has a MetaBean of its own
+     */
+    public boolean isRelationship() {
+        return metaBean != null;
+    }
+
+    /**
+     * Set the type of this property.
+     * @param type to set
+     */
+    public void setType(Type type) {
+        this.type = type;
+    }
+
+    /**
+     * Get the type of this property.
+     * @return
+     */
+    public Type getType() {
+        return type;
+    }
+
+    /**
+     * Resolve the type of this property to a class.
+     * @return Class, <code>null</code> if cannot be determined
+     */
+    public Class<?> getTypeClass() {
+        Type targetType = type instanceof DynaType ? ((DynaType) type)
+                .getRawType() : type;
+        if (targetType == null) {
+            return null;
+        }
+        Type assigningType = getParentMetaBean() == null ? null
+                : getParentMetaBean().getBeanClass();
+        return TypeUtils.getRawType(targetType, assigningType);
+    }
+
+    /**
+     * Get the name of this property.
+     * @return String
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Learn whether this property is considered mandatory.
+     * @return <code>true</code> if the <code>MANDATORY</code> feature is set to <code>true</code>.
+     * @see {@link Features.Property#MANDATORY}
+     */
+    public boolean isMandatory() {
+        return getFeature(MANDATORY, Boolean.FALSE).booleanValue();
+    }
+
+    /**
+     * Set this property as being mandatory (or not).
+     * @param mandatory
+     * @see {@link Features.Property#MANDATORY}
+     */
+    public void setMandatory(boolean mandatory) {
+        putFeature(MANDATORY, Boolean.valueOf(mandatory));
+    }
+
+    /**
+     * Get javascript validations of this property.
+     * @return String[]
+     * @deprecated
+     */
+    @Deprecated // remove this method?
+    public String[] getJavaScriptValidations() {
+        return getFeature(JAVASCRIPT_VALIDATION_FUNCTIONS);
+    }
+
+    /**
+     * Set the name of this property.
+     * @param name to set
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MetaProperty clone() throws CloneNotSupportedException {
+        return (MetaProperty) super.clone();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return "MetaProperty{" + "name='" + name + '\'' + ", type=" + type + '}';
+    }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/Validation.java b/trunk/bval-core/src/main/java/org/apache/bval/model/Validation.java
new file mode 100644
index 0000000..a81e731
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/Validation.java
@@ -0,0 +1,30 @@
+/*
+ *  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.bval.model;
+
+/**
+ * Description: Interface for a single validation <br/>
+ */
+public interface Validation {
+    /**
+     * Perform a single validation routine.
+     * Validate the object or property according to the current ValidationContext.
+     *
+     * @param context - to access the property, value, constraints
+     */
+    <T extends ValidationListener> void validate(ValidationContext<T> context);
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/ValidationContext.java b/trunk/bval-core/src/main/java/org/apache/bval/model/ValidationContext.java
new file mode 100644
index 0000000..900444a
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/ValidationContext.java
@@ -0,0 +1,135 @@
+/*
+ *  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.bval.model;
+
+import org.apache.bval.util.AccessStrategy;
+
+/**
+ * Description: Interface of the context that holds all state information
+ * during the validation process<br/>
+ */
+public interface ValidationContext<T extends ValidationListener> {
+    /**
+     * Get the property value.
+     * @return {@link Object}
+     */
+    Object getPropertyValue();
+
+    /**
+     * Get the value by using the given access strategy.
+     * @param access
+     * @return {@link Object}
+     */
+    Object getPropertyValue(AccessStrategy access);
+
+    /**
+     * Get the property name.
+     * @return {@link String}
+     */
+    String getPropertyName();
+
+    /**
+     * Get the {@link ValidationListener}.
+     * @return T
+     */
+    T getListener();
+
+    /**
+     * Get the bean.
+     * @return {@link Object}
+     */
+    Object getBean();
+
+    /**
+     * Get the model meta-bean.
+     * @return {@link MetaBean}
+     */
+    MetaBean getMetaBean();
+
+    /**
+     * Set the model meta-bean.
+     * @param metaBean
+     */
+    void setMetaBean(MetaBean metaBean);
+
+    /**
+     * Get the model meta-property.
+     * @return {@link MetaProperty}
+     */
+    MetaProperty getMetaProperty();
+
+    /**
+     * Set the bean.
+     * @param bean
+     */
+    void setBean(Object bean);
+
+    /**
+     * Avoid recursion by recording the current state of this context as having been validated.
+     * <p/>
+     *
+     * @return true when this state had not already been recorded
+     */
+    boolean collectValidated();
+
+    /**
+     * Set the current bean/metabean.
+     * @param aBean
+     * @param aMetaBean
+     */
+    void setBean(Object aBean, MetaBean aMetaBean);
+
+    /**
+     * Set the current meta-property.
+     * @param metaProperty
+     */
+    void setMetaProperty(MetaProperty metaProperty);
+
+    /**
+     * Step deeper into association at 'prop' 
+     * @param prop
+     * @param access
+     */
+    void moveDown(MetaProperty prop, AccessStrategy access);
+
+    /**
+     * Step out from a validation of associated objects.
+     * @param bean
+     * @param metaBean
+     */
+    void moveUp(Object bean, MetaBean metaBean);
+
+    /**
+     * Set the index of the object currently validated into the context.
+     * used to create the propertyPath with [index] information for collections.
+     * @param index
+     */
+    void setCurrentIndex(Integer index);
+
+    /**
+     * set the key of the object in a map currently validated into the context.
+     * used to create the propertyPath with [key] information for maps.
+     * @param key
+     */
+    void setCurrentKey(Object key);
+
+    /**
+     * Get the current access strategy.
+     * @return {@link AccessStrategy}
+     */
+    AccessStrategy getAccess();
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/model/ValidationListener.java b/trunk/bval-core/src/main/java/org/apache/bval/model/ValidationListener.java
new file mode 100644
index 0000000..1a41deb
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/model/ValidationListener.java
@@ -0,0 +1,101 @@
+/*
+ *  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.bval.model;
+
+
+import java.io.Serializable;
+
+
+/**
+ * Description: The interface to collect errors found during validation<br/>
+ */
+public interface ValidationListener {
+    /**
+     * Simple API to add an error reason during validation.
+     * Error notification added from a {@link Validation} with context information
+     * taken from the given {@link ValidationContext}.
+     *
+     * @param reason  a constant describing the reason. This is normally the key of the
+     *                feature that was violated in the object 'owner' for property 'propertyName'
+     * @param context - contains
+     *                bean =         the object that contains the error (owner)
+     *                propertyName = the Name of the attribute that caused the error
+     */
+    <T extends ValidationListener> void addError(String reason, ValidationContext<T> context);
+
+    /** Alternative method to add a fully initialized {@link ValidationListener.Error} object. */
+    <T extends ValidationListener> void addError(Error error, ValidationContext<T> context);
+
+    /**
+     * An object holding a single validation constraint violation
+     * found during the validation process.
+     */
+    public class Error implements Serializable {
+        private static final long serialVersionUID = 1L;
+
+        /** Reason */
+        final String reason;
+        /** Owner */
+        final Object owner;
+        /** Property name*/
+        final String propertyName;
+
+        /**
+         * Create a new Error instance.
+         * @param aReason
+         * @param aOwner
+         * @param aPropertyName
+         */
+        public Error(String aReason, Object aOwner, String aPropertyName) {
+            this.reason = aReason;
+            this.owner = aOwner;
+            this.propertyName = aPropertyName;
+        }
+
+        /**
+         * Get the reason.
+         * @return String
+         */
+        public String getReason() {
+            return reason;
+        }
+
+        /**
+         * Get the owner.
+         * @return Object
+         */
+        public Object getOwner() {
+            return owner;
+        }
+
+        /**
+         * Get the propertyName.
+         * @return String
+         */
+        public String getPropertyName() {
+            return propertyName;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public String toString() {
+            return "Error{" + "reason='" + reason + '\'' + ", propertyName='" +
+                  propertyName + '\'' + '}';
+        }
+    }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/routines/EMailValidationUtils.java b/trunk/bval-core/src/main/java/org/apache/bval/routines/EMailValidationUtils.java
new file mode 100644
index 0000000..086da02
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/routines/EMailValidationUtils.java
@@ -0,0 +1,74 @@
+/*
+ *  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.bval.routines;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Description: holds the regexp to validate an email address<br>
+ * User: roman.stumm<br>
+ * Date: 17.06.2010<br>
+ * Time: 10:40:59<br>
+ */
+public class EMailValidationUtils {
+    private static String ATOM = "[^\\x00-\\x1F^\\(^\\)^\\<^\\>^\\@^\\,^\\;^\\:^\\\\^\\\"^\\.^\\[^\\]^\\s]";
+    private static String DOMAIN = "(" + ATOM + "+(\\." + ATOM + "+)*";
+    private static String IP_DOMAIN = "\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\]";
+    public static final java.util.regex.Pattern DEFAULT_EMAIL_PATTERN;
+
+    static {
+        DEFAULT_EMAIL_PATTERN =
+            java.util.regex.Pattern.compile("^" + ATOM + "+(\\." + ATOM + "+)*@" + DOMAIN + "|" + IP_DOMAIN + ")$",
+                java.util.regex.Pattern.CASE_INSENSITIVE);
+    }
+
+    /**
+     * Learn whether a given object is a valid email address.
+     * 
+     * @param value
+     *            to check
+     * @return <code>true</code> if the validation passes
+     */
+    public static boolean isValid(Object value) {
+        return isValid(value, DEFAULT_EMAIL_PATTERN);
+    }
+
+    /**
+     * Learn whether a particular value matches a given pattern per
+     * {@link Matcher#matches()}.
+     * 
+     * @param value
+     * @param aPattern
+     * @return <code>true</code> if <code>value</code> was a <code>String</code>
+     *         matching <code>aPattern</code>
+     */
+    // TODO it would seem to make sense to move or reduce the visibility of this
+    // method as it is more general than email.
+    public static boolean isValid(Object value, Pattern aPattern) {
+        if (value == null)
+            return true;
+        if (!(value instanceof CharSequence))
+            return false;
+        CharSequence seq = (CharSequence) value;
+        if (seq.length() == 0)
+            return true;
+        Matcher m = aPattern.matcher(seq);
+        return m.matches();
+    }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/AccessStrategy.java b/trunk/bval-core/src/main/java/org/apache/bval/util/AccessStrategy.java
new file mode 100644
index 0000000..be97ebc
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/AccessStrategy.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.bval.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Type;
+
+/**
+ * Description: abstract class to encapsulate different strategies
+ * to get the value of a Property.  This class is designed such that
+ * subclasses are intended to know internally to which property they refer,
+ * with only the particular target instance being externally required
+ * to calculate the property's value.  One intent of this design is
+ * that the notion of the very definition of a property is abstracted
+ * along with the mechanism for accessing that property.<br/>
+ */
+public abstract class AccessStrategy {
+    /**
+     * Get the value from the given instance.
+     * @param instance
+     * @return the value
+     * @throws IllegalArgumentException in case of an error
+     */
+    public abstract Object get(Object instance);
+
+    /**
+     * Get the Java program {@link ElementType} used by this {@link AccessStrategy}
+     * to determine property values.
+     * @return ElementType
+     */
+    public abstract ElementType getElementType();
+
+    /**
+     * Get the type of the property
+     * @return Type
+     */
+    public abstract Type getJavaType();
+
+    /**
+     * Get a name representative of this property.
+     * @return String
+     */
+    public abstract String getPropertyName();
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/BValVersion.java b/trunk/bval-core/src/main/java/org/apache/bval/util/BValVersion.java
new file mode 100644
index 0000000..49c0a38
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/BValVersion.java
@@ -0,0 +1,195 @@
+/*
+ * 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.bval.util;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/**
+ * This class contains version information for BVal.
+ * It uses Ant's filter tokens to convert the template into a java
+ * file with current information.
+ */
+public class BValVersion {
+
+    /** Project name */
+    public static final String PROJECT_NAME = "Apache BVal";
+    /** Unique id of the current project/version/revision */
+    public static final String PROJECT_ID;
+    /** Version number */
+    public static final String VERSION_NUMBER;
+    /** Major release number */
+    public static final int MAJOR_RELEASE;
+    /** Minor release number */
+    public static final int MINOR_RELEASE;
+    /** Patch/point release number */
+    public static final int PATCH_RELEASE;
+    /** Release status */
+    public static final String RELEASE_STATUS;
+    /** Version control revision number */
+    public static final String REVISION_NUMBER;
+
+    static {
+        Properties revisionProps = new Properties();
+        try {
+            InputStream in = BValVersion.class.getResourceAsStream
+                ("/META-INF/org.apache.bval.revision.properties");
+            if (in != null) {
+                try {
+                    revisionProps.load(in);
+                } finally {
+                    in.close();
+                }
+            }
+        } catch (IOException ioe) {
+        }
+
+        String vers = revisionProps.getProperty("project.version");
+        if (vers == null || "".equals(vers.trim()))
+            vers = "0.0.0";
+        VERSION_NUMBER = vers;
+
+        StringTokenizer tok = new StringTokenizer(VERSION_NUMBER, ".-");
+        int major, minor, patch;
+        try {
+            major = tok.hasMoreTokens() ? Integer.parseInt(tok.nextToken()) : 0;
+        } catch (Exception e) {
+            major = 0;
+        }
+
+        try {
+            minor = tok.hasMoreTokens() ? Integer.parseInt(tok.nextToken()) : 0;
+        } catch (Exception e) {
+            minor = 0;
+        }
+
+        try {
+            patch = tok.hasMoreTokens() ? Integer.parseInt(tok.nextToken()) : 0;
+        } catch (Exception e) {
+            patch = 0;
+        }
+
+        String revision = revisionProps.getProperty("svn.revision");
+        if (revision == null || "".equals(revision.trim())) {
+            revision = "unknown";
+        } else {
+            tok = new StringTokenizer(revision, ":");
+            String strTok = null;
+            while (tok.hasMoreTokens()) {
+                try {
+                    strTok = tok.nextToken();
+                } catch (Exception e) {
+                }
+            }
+            if (strTok != null)
+                revision = strTok;
+        }
+
+        MAJOR_RELEASE = major;
+        MINOR_RELEASE = minor;
+        PATCH_RELEASE = patch;
+        RELEASE_STATUS = tok.hasMoreTokens() ? tok.nextToken("!") : "";
+        REVISION_NUMBER = revision;
+        PROJECT_ID = PROJECT_NAME + " " + VERSION_NUMBER + "-r" + REVISION_NUMBER;
+    }
+
+    /**
+     * Get the project version number.
+     * @return String
+     */
+    public static String getVersion() {
+        return VERSION_NUMBER;
+    }
+
+    /**
+     * Get the version control revision number.
+     * @return String
+     */
+    public static String getRevision() {
+        return REVISION_NUMBER;
+    }
+
+    /**
+     * Get the project name.
+     * @return String
+     */
+    public static String getName() {
+        return PROJECT_NAME;
+    }
+
+    /**
+     * Get the fully-qualified project id.
+     * @return String
+     */
+    public static String getID() {
+        return PROJECT_ID;
+    }
+
+    /**
+     * Main method of this class that prints the {@link #toString()} to <code>System.out</code>.
+     * @param args ignored
+     */
+    public static void main(String [] args) {
+        System.out.println(new BValVersion().toString());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        StringBuilder buf = new StringBuilder(80 * 40);
+        appendBanner(buf);
+        buf.append("\n");
+
+        appendProperty("os.name", buf).append("\n");
+        appendProperty("os.version", buf).append("\n");
+        appendProperty("os.arch", buf).append("\n\n");
+
+        appendProperty("java.version", buf).append("\n");
+        appendProperty("java.vendor", buf).append("\n\n");
+
+        buf.append("java.class.path:\n");
+        StringTokenizer tok = new StringTokenizer(
+            PrivilegedActions.getProperty("java.class.path"));
+        while (tok.hasMoreTokens()) {
+            buf.append("\t").append(tok.nextToken());
+            buf.append("\n");
+        }
+        buf.append("\n");
+
+        appendProperty("user.dir", buf).append("\n");
+        return buf.toString();
+    }
+
+    private void appendBanner(StringBuilder buf) {
+        buf.append("Project").append(": ").append(getName());
+        buf.append("\n");
+        buf.append("Version").append(": ").append(getVersion());
+        buf.append("\n");
+        buf.append("Revision").append(": ").append(getRevision());
+        buf.append("\n");
+    }
+
+    private StringBuilder appendProperty(String prop, StringBuilder buf) {
+        return buf.append(prop).append(": ").append(
+            PrivilegedActions.getProperty(prop));
+    }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/FieldAccess.java b/trunk/bval-core/src/main/java/org/apache/bval/util/FieldAccess.java
new file mode 100644
index 0000000..9cbc31e
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/FieldAccess.java
@@ -0,0 +1,113 @@
+/*
+ *  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.bval.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Description: direct field access strategy.<br/>
+ */
+public class FieldAccess extends AccessStrategy {
+
+    private final Field field;
+
+    /**
+     * Create a new FieldAccess instance.
+     * @param field
+     */
+    public FieldAccess(final Field field) {
+        this.field = field;
+        if (!field.isAccessible()) {
+            run(new PrivilegedAction<Void>() {
+                public Void run() {
+                    field.setAccessible(true);
+                    return null;
+                }
+            });
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object get(final Object instance) {
+        try {
+            return field.get(instance);
+        } catch (IllegalAccessException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ElementType getElementType() {
+        return ElementType.FIELD;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Type getJavaType() {
+        return field.getGenericType();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getPropertyName() {
+        return field.getName();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return field.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        FieldAccess that = (FieldAccess) o;
+
+        return field.equals(that.field);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        return field.hashCode();
+    }
+
+    private static <T> T run(PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/IndexedAccess.java b/trunk/bval-core/src/main/java/org/apache/bval/util/IndexedAccess.java
new file mode 100644
index 0000000..315c25e
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/IndexedAccess.java
@@ -0,0 +1,120 @@
+/**
+ *  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.bval.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Array;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+/**
+ * {@link AccessStrategy} to get an indexed member of an {@link Iterable} or
+ * array object.
+ */
+public class IndexedAccess extends AccessStrategy {
+    private static final TypeVariable<?> ITERABLE_TYPE = Iterable.class.getTypeParameters()[0];
+
+    /**
+     * Get the Java element type of a particular container type.
+     * 
+     * @param containerType
+     * @return Type or <code>null</code> if <code>containerType</code> is not
+     *         some type of {@link Iterable} or array
+     */
+    public static Type getJavaElementType(Type containerType) {
+        if (TypeUtils.isArrayType(containerType)) {
+            return TypeUtils.getArrayComponentType(containerType);
+        }
+        if (TypeUtils.isAssignable(containerType, Iterable.class)) {
+            Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(containerType, Iterable.class);
+            return typeArguments.containsKey(ITERABLE_TYPE) ? typeArguments.get(ITERABLE_TYPE) : Object.class;
+        }
+        return null;
+    }
+
+    private Type containerType;
+    private Integer index;
+
+    /**
+     * Create a new IndexedAccessStrategy instance.
+     * 
+     * @param containerType
+     * @param index
+     */
+    public IndexedAccess(Type containerType, Integer index) {
+        super();
+        this.containerType = containerType;
+        this.index = index;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Object get(Object instance) {
+        if (index == null) {
+            throw new UnsupportedOperationException("Cannot read null index");
+        }
+        if (instance != null && instance.getClass().isArray()) {
+            if (Array.getLength(instance) - index > 0) {
+                return Array.get(instance, index);
+            }
+        } else if (instance instanceof List<?>) {
+            List<?> list = (List<?>) instance;
+            if (list.size() - index > 0) {
+                return list.get(index);
+            }
+        } else if (instance instanceof Iterable<?>) {
+            int i = 0;
+            for (Object o : (Iterable<?>) instance) {
+                if (++i == index) {
+                    return o;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ElementType getElementType() {
+        return ElementType.METHOD;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Type getJavaType() {
+        return getJavaElementType(containerType);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getPropertyName() {
+        return String.format("[%d]", index);
+    }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/KeyedAccess.java b/trunk/bval-core/src/main/java/org/apache/bval/util/KeyedAccess.java
new file mode 100644
index 0000000..6050502
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/KeyedAccess.java
@@ -0,0 +1,121 @@
+/**
+ *  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.bval.util;
+
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Map;
+
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+/**
+ * {@link AccessStrategy} to get a keyed value from a {@link Map}. Contains
+ * special handling when a string key is used against a container type whose key
+ * parameter is not assignable from {@link String}: against a map whose key type
+ * is an enum class, it will be interpreted as a named enum constant; other key
+ * types will be compared via {@link Object#toString()}.
+ */
+public class KeyedAccess extends AccessStrategy {
+    private static final TypeVariable<?>[] MAP_TYPEVARS = Map.class.getTypeParameters();
+
+    /**
+     * Get the Java element type of a particular container type.
+     * 
+     * @param containerType
+     * @return Type or <code>null</code> if <code>containerType</code> is not
+     *         some kind of {@link Map}
+     */
+    public static Type getJavaElementType(Type containerType) {
+        if (TypeUtils.isAssignable(containerType, Map.class)) {
+            Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(containerType, Map.class);
+            return typeArguments.containsKey(MAP_TYPEVARS[1]) ? typeArguments.get(MAP_TYPEVARS[1]) : Object.class;
+        }
+        return null;
+    }
+
+    private Type containerType;
+    private Object key;
+
+    /**
+     * Create a new KeyedAccess instance.
+     * 
+     * @param containerType
+     * @param key
+     */
+    public KeyedAccess(Type containerType, Object key) {
+        super();
+        this.containerType = containerType;
+        this.key = key;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Object get(Object instance) {
+        if (instance instanceof Map<?, ?>) {
+            Map<?, ?> map = (Map<?, ?>) instance;
+            Map<TypeVariable<?>, Type> typeArguments = TypeUtils.getTypeArguments(containerType, Map.class);
+            Type keyType = typeArguments.get(MAP_TYPEVARS[0]);
+            if (key == null || keyType == null || TypeUtils.isInstance(key, keyType)) {
+                return map.get(key);
+            }
+            if (key instanceof String) {
+                String name = (String) key;
+                Class<?> rawKeyType = TypeUtils.getRawType(keyType, containerType);
+                if (rawKeyType.isEnum()) {
+                    @SuppressWarnings({ "unchecked", "rawtypes" })
+                    final Object result = map.get(Enum.valueOf((Class<? extends Enum>) rawKeyType, name));
+                    return result;
+                }
+                for (Map.Entry<?, ?> e : map.entrySet()) {
+                    if (name.equals(e.getKey())) {
+                        return e.getValue();
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ElementType getElementType() {
+        return ElementType.METHOD;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Type getJavaType() {
+        Type result = getJavaElementType(containerType);
+        return result == null ? Object.class : result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getPropertyName() {
+        return String.format("[%s]", key);
+    }
+
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/MethodAccess.java b/trunk/bval-core/src/main/java/org/apache/bval/util/MethodAccess.java
new file mode 100644
index 0000000..2583fbb
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/MethodAccess.java
@@ -0,0 +1,155 @@
+/*
+ *  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.bval.util;
+
+import java.beans.Introspector;
+import java.lang.annotation.ElementType;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Description: invoke a zero-argument method (getter)<br/>
+ */
+public class MethodAccess extends AccessStrategy {
+    private final Method method;
+    private final String propertyName;
+
+    /**
+     * Create a new MethodAccess instance.
+     * @param method
+     */
+    public MethodAccess(Method method) {
+        this(getPropertyName(method), method);
+    }
+
+    /**
+     * Create a new MethodAccess instance.
+     * @param propertyName
+     * @param method
+     */
+    public MethodAccess(String propertyName, final Method method) {
+        this.method = method;
+        this.propertyName = propertyName;
+        if (!method.isAccessible()) {
+            run( new PrivilegedAction<Void>() {
+                public Void run() {
+                    method.setAccessible(true);
+                    return null;
+                }
+            });
+        }
+    }
+
+    /**
+     * Process bean properties getter by applying the JavaBean naming conventions.
+     *
+     * @param member the member for which to get the property name.
+     * @return The bean method name with the "is" or "get" prefix stripped off, <code>null</code>
+     *         the method name id not according to the JavaBeans standard.
+     */
+    public static String getPropertyName(Method member) {
+        String name = null;
+        String methodName = member.getName();
+        if (methodName.startsWith("is")) {
+            name = Introspector.decapitalize(methodName.substring(2));
+        } /* else if ( methodName.startsWith("has")) {
+				name = Introspector.decapitalize( methodName.substring( 3 ) );
+			} */
+        // setter annotation is NOT supported in the spec
+        /*  else if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) {
+           propName = Introspector.decapitalize(method.getName().substring(3));
+       } */
+        else if (methodName.startsWith("get")) {
+            name = Introspector.decapitalize(methodName.substring(3));
+        }
+        return name;
+    }
+
+    /**
+     * {@inheritDoc}
+     * normally the propertyName of the getter method, e.g.<br>
+     * method: getName() -> propertyName: name<br>
+     * method: isValid() -> propertyName: valid<br>
+     */
+    public String getPropertyName() {
+        return propertyName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object get(final Object instance) {
+        try {
+            return method.invoke(instance);
+        } catch (IllegalAccessException e) {
+            throw new IllegalArgumentException(e);
+        } catch (InvocationTargetException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ElementType getElementType() {
+        return ElementType.METHOD;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Type getJavaType() {
+        return method.getGenericReturnType();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return method.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        MethodAccess that = (MethodAccess) o;
+
+        return method.equals(that.method);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        return method.hashCode();
+    }
+
+    private static <T> T run(PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/PrivilegedActions.java b/trunk/bval-core/src/main/java/org/apache/bval/util/PrivilegedActions.java
new file mode 100644
index 0000000..26c0936
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/PrivilegedActions.java
@@ -0,0 +1,189 @@
+/*
+ *  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.bval.util;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+import org.apache.commons.lang3.ClassUtils;
+
+/**
+ * Description: utility methods to perform actions with AccessController or without. <br/>
+ */
+public class PrivilegedActions {
+    private static String lineSeparator = null;
+    private static String pathSeparator = null;
+
+    /**
+     * Return the value of the "line.separator" system property.
+     * 
+     * Requires security policy: 
+     *   'permission java.util.PropertyPermission "read";'
+     */
+    @Deprecated // unused method - will remove in future release
+    public static final String getLineSeparator() {
+        if (lineSeparator == null) {
+            lineSeparator =
+                AccessController.doPrivileged(new PrivilegedAction<String>() {
+                    public String run() {
+                        return System.getProperty("line.separator");
+                    }
+                });
+        }
+        return lineSeparator;
+    }
+
+    /**
+     * Return the value of the "path.separator" system property.
+     * 
+     * Requires security policy:
+     *   'permission java.util.PropertyPermission "read";'
+     */
+    @Deprecated // unused method - will remove in future release
+    public static final String getPathSeparator() {
+        if (pathSeparator == null) {
+            pathSeparator =
+                AccessController.doPrivileged(new PrivilegedAction<String>() {
+                    public String run() {
+                        return System.getProperty("path.separator");
+                    }
+                });
+        }
+        return pathSeparator;
+    }
+
+    /**
+     * Perform action with AccessController.doPrivileged() if security if enabled.
+     *
+     * @param action - the action to run
+     * @return result of running the action
+     */
+    // should not be called by just anyone; do not increase access
+    private static <T> T run(PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+
+    /**
+     * Perform action with AccessController.doPrivileged() if security if enabled.
+     *
+     * @param action - the action to run
+     * @return result of running the action
+     */
+    // should not be called by just anyone; do not increase access
+    private static <T> T run(final PrivilegedExceptionAction<T> action) throws PrivilegedActionException, Exception {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+
+    /**
+     * Perform AccessController.doPrivileged() action for ClassUtil.getClass()
+     * 
+     * @return Class
+     * @exception Exception
+     */
+    public static Class<?> getClass(final ClassLoader classLoader, final String className) throws Exception {
+        return run(new PrivilegedExceptionAction<Class<?>>() {
+            public Class<?> run() throws Exception {
+                return ClassUtils.getClass(classLoader, className, true);
+            }
+        });
+    }
+
+    /**
+     * Return a PrivilegedAction object for clazz.getDeclaredMethod().invoke().
+     * 
+     * Requires security policy
+     *  'permission java.lang.RuntimePermission "accessDeclaredMembers";'
+     *  'permission java.lang.reflect.ReflectPermission "suppressAccessChecks";'
+     *   
+     * @return Object
+     * @exception IllegalAccessException, InvocationTargetException
+     */
+    public static Object getAnnotationValue(final Annotation annotation, final String name)
+          throws IllegalAccessException, InvocationTargetException {
+        return run(new PrivilegedAction<Object>() {
+            public Object run() {
+                Method valueMethod;
+                try {
+                    valueMethod = annotation.annotationType().getDeclaredMethod(name);
+                } catch (NoSuchMethodException ex) {
+                    // do nothing
+                    valueMethod = null;
+                }
+                if (null != valueMethod) {
+                    try {
+                        valueMethod.setAccessible(true);
+                        return valueMethod.invoke(annotation);
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+                return null;
+            }
+        });
+    }
+
+    /**
+     * Return a PrivilegeAction object for clazz.getClassloader().
+     * 
+     * Requires security policy:
+     *   'permission java.lang.RuntimePermission "getClassLoader";'
+     *   
+     * @return Classloader
+     */
+    public static ClassLoader getClassLoader(final Class<?> clazz) {
+        return run(new PrivilegedAction<ClassLoader>() {
+            public ClassLoader run() {
+                ClassLoader cl = Thread.currentThread().getContextClassLoader();
+                if (cl == null) {
+                    cl = clazz.getClassLoader();
+                }
+                return cl;
+            }
+        });
+    }
+
+    /**
+     * Return a PrivilegeAction object for System.getProperty().
+     * 
+     * Requires security policy:
+     *   'permission java.util.PropertyPermission "read";'
+     *   
+     * @return String
+     */
+    public static final String getProperty(final String name) {
+        return run(new PrivilegedAction<String>() {
+            public String run() {
+                return System.getProperty(name);
+            }
+        });
+    }
+
+}
+
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/PropertyAccess.java b/trunk/bval-core/src/main/java/org/apache/bval/util/PropertyAccess.java
new file mode 100644
index 0000000..d79235e
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/PropertyAccess.java
@@ -0,0 +1,218 @@
+/*
+ *  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.bval.util;
+
+import org.apache.commons.beanutils.PropertyUtils;
+
+import java.beans.PropertyDescriptor;
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+/**
+ * Description: Undefined dynamic strategy (FIELD or METHOD access) Uses PropertyUtils or tries to determine field to
+ * access the value<br/>
+ */
+public class PropertyAccess extends AccessStrategy {
+    private final Class<?> beanClass;
+    private final String propertyName;
+    private Field rememberField;
+
+    /**
+     * Create a new PropertyAccess instance.
+     * 
+     * @param clazz
+     * @param propertyName
+     */
+    public PropertyAccess(Class<?> clazz, String propertyName) {
+        this.beanClass = clazz;
+        this.propertyName = propertyName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ElementType getElementType() {
+        return rememberField != null ? ElementType.FIELD : ElementType.METHOD;
+    }
+
+    private static Object getPublicProperty(Object bean, String property) throws InvocationTargetException,
+        NoSuchMethodException, IllegalAccessException {
+        if (bean instanceof Map<?, ?>) {
+            return ((Map<?, ?>) bean).get(property);
+        } else { // supports DynaBean and standard Objects
+            return PropertyUtils.getSimpleProperty(bean, property);
+        }
+    }
+
+    /**
+     * Get a named property from <code>bean</code>.
+     * 
+     * @param bean
+     * @param propertyName
+     * @return Object found
+     * @throws InvocationTargetException
+     * @throws NoSuchMethodException
+     * @throws IllegalAccessException
+     */
+    public static Object getProperty(Object bean, String propertyName) throws InvocationTargetException,
+        NoSuchMethodException, IllegalAccessException {
+        return new PropertyAccess(bean.getClass(), propertyName).get(bean);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return "Property{" + beanClass.getName() + '.' + propertyName + '}';
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Type getJavaType() {
+        Type result = getTypeInner();
+        return result == null ? Object.class : result;
+    }
+
+    /**
+     * Learn whether this {@link PropertyAccess} references a known property.
+     * 
+     * @return boolean
+     */
+    public boolean isKnown() {
+        return getTypeInner() != null;
+    }
+
+    /**
+     * Find out what, if any, type can be calculated.
+     * 
+     * @return type found or <code>null</code>
+     */
+    private Type getTypeInner() {
+        if (rememberField != null) {
+            return rememberField.getGenericType();
+        }
+        Method readMethod = getPropertyReadMethod(propertyName, beanClass);
+        if (readMethod != null) {
+            return readMethod.getGenericReturnType();
+        }
+        Field fld = getField(propertyName, beanClass);
+        if (fld != null) {
+            cacheField(fld);
+            return rememberField.getGenericType();
+        }
+        return null;
+    }
+
+    private static Method getPropertyReadMethod(String propertyName, Class<?> beanClass) {
+        for (PropertyDescriptor each : PropertyUtils.getPropertyDescriptors(beanClass)) {
+            if (each.getName().equals(propertyName)) {
+                return each.getReadMethod();
+            }
+        }
+        return null;
+    }
+
+    private static Field getField(String propertyName, Class<?> beanClass) {
+        try { // try public field
+            return beanClass.getField(propertyName);
+        } catch (NoSuchFieldException ex2) {
+            // search for private/protected field up the hierarchy
+            Class<?> theClass = beanClass;
+            while (theClass != null) {
+                try {
+                    return theClass.getDeclaredField(propertyName);
+                } catch (NoSuchFieldException ex3) {
+                    // do nothing
+                }
+                theClass = theClass.getSuperclass();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getPropertyName() {
+        return propertyName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object get(Object bean) {
+        try {
+            if (rememberField != null) { // cache field of previous access
+                return rememberField.get(bean);
+            }
+            try { // try public method
+                return getPublicProperty(bean, propertyName);
+            } catch (NoSuchMethodException ex) {
+                return getFieldValue(bean);
+            }
+        } catch (IllegalArgumentException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IllegalArgumentException("cannot access " + propertyName, e);
+        }
+    }
+
+    private Object getFieldValue(Object bean) throws IllegalAccessException {
+        Field field = getField(propertyName, beanClass);
+        if (field != null) {
+            cacheField(field);
+            return rememberField.get(bean);
+        }
+        throw new IllegalArgumentException("cannot access field " + propertyName);
+    }
+
+    private void cacheField(Field field) {
+        if (!field.isAccessible()) {
+            field.setAccessible(true);
+        }
+        this.rememberField = field;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        PropertyAccess that = (PropertyAccess) o;
+
+        return beanClass.equals(that.beanClass) && propertyName.equals(that.propertyName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int hashCode() {
+        int result;
+        result = beanClass.hashCode();
+        result = 31 * result + propertyName.hashCode();
+        return result;
+    }
+}
diff --git a/trunk/bval-core/src/main/java/org/apache/bval/util/ValidationHelper.java b/trunk/bval-core/src/main/java/org/apache/bval/util/ValidationHelper.java
new file mode 100644
index 0000000..35677c8
--- /dev/null
+++ b/trunk/bval-core/src/main/java/org/apache/bval/util/ValidationHelper.java
@@ -0,0 +1,236 @@
+/*
+ * 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.bval.util;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.bval.DynamicMetaBean;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.model.Validation;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+
+
+/**
+ * Stateless helper methods used by the validators.
+ * 
+ * @author Carlos Vara
+ */
+public class ValidationHelper {
+
+    /**
+     * Interface implemented by the call-back object passed to
+     * {@link ValidationHelper#validateContext(ValidationContext, ValidateCallback, boolean)}
+     * . Its {@link #validate()} method will be called accordingly for every
+     * dispatch.
+     */
+    public static interface ValidateCallback {
+        void validate();
+    }
+
+    /**
+     * validate a complex 'bean' with related beans according to
+     * validation rules in 'metaBean'
+     * 
+     * @param context
+     *            - the context is initialized with: <br>
+     *            &nbsp;&nbsp;bean - the root object start validation at
+     *            or a collection of root objects <br>
+     *            &nbsp;&nbsp;metaBean - the meta information for the root
+     *            object(s)
+     * @param context
+     *            The current validation context.
+     */
+    static public void validateContext(ValidationContext<?> context, ValidateCallback s, boolean treatMapsLikeBeans) {
+        if (context.getBean() != null) {
+            if (!treatMapsLikeBeans && context.getBean() instanceof Map<?, ?>) {
+                validateMapInContext(context, s);
+            } else if (context.getBean() instanceof Iterable<?>) {
+                validateIterableInContext(context, s);
+            } else if (context.getBean() instanceof Object[]) {
+                validateArrayInContext(context, s);
+            } else { // to One Bean (or Map like Bean)
+                validateBeanInContext(context, s);
+            }
+        }
+    }
+
+    /**
+     * Validates a single object.
+     * 
+     * @param <VL>
+     * @param context
+     *            The validation context, its current bean must be a single
+     *            object.
+     * @param s
+     */
+    static protected <VL extends ValidationListener> void validateBeanInContext(ValidationContext<VL> context, ValidateCallback s) {
+        if (getDynamicMetaBean(context) != null) {
+            context.setMetaBean(getDynamicMetaBean(context).resolveMetaBean(context.getBean()));
+        }
+        s.validate();
+    }
+
+    /**
+     * Iterates the values of an array, setting the current context
+     * appropriately and validating each value.
+     * 
+     * @param <VL>
+     * @param context
+     *            The validation context, its current bean must be an array.
+     */
+    static protected <VL extends ValidationListener> void validateArrayInContext(ValidationContext<VL> context, ValidateCallback s) {
+        int index = 0;
+        DynamicMetaBean dyn = getDynamicMetaBean(context);
+        Object[] array = (Object[]) context.getBean();
+        MetaBean metaBean = context.getMetaBean();
+        context.setCurrentIndex(null);
+        try {
+            for (Object each : array) {
+                context.setCurrentIndex(index++);
+                if (each == null) {
+                    continue; // Null values are not validated
+                }
+                if (dyn != null) {
+                    context.setBean(each, dyn.resolveMetaBean(each));
+                } else {
+                    context.setBean(each);
+                }
+                s.validate();
+            }
+        } finally {
+            context.moveUp(array, metaBean);
+        }
+    }
+
+    /**
+     * Iterates the values of an {@link Iterable} object, setting the current
+     * context appropriately and validating each value.
+     * 
+     * @param <VL>
+     * @param context
+     *            The validation context, its current bean must implement
+     *            {@link Iterable}.
+     */
+    static protected <VL extends ValidationListener> void validateIterableInContext(ValidationContext<VL> context, ValidateCallback s) {
+
+        final boolean positional = context.getBean() instanceof List<?>;
+        int index = 0;
+        Iterable<?> iterable = (Iterable<?>) context.getBean();
+        MetaBean metaBean = context.getMetaBean();
+        context.setCurrentIndex(null);
+
+        try {
+            // jsr303 spec: Each object provided by the iterator is validated.
+            final DynamicMetaBean dyn = getDynamicMetaBean(context);
+            for (Object each : iterable) {
+                if (positional) {
+                    context.setCurrentIndex(index++);
+                }
+                if (each == null) {
+                    continue; // Null values are not validated
+                }
+                if (dyn != null) {
+                    context.setBean(each, dyn.resolveMetaBean(each));
+                } else {
+                    context.setBean(each);
+                }
+                s.validate();
+            }
+        } finally {
+            context.moveUp(iterable, metaBean);
+        }
+    }
+
+    /**
+     * Iterates the values of a {@link Map}, setting the current context
+     * appropriately and validating each value.
+     * 
+     * @param <VL>
+     * @param context
+     *            The validation context, its current bean must implement
+     *            {@link Map}.
+     */
+    static protected <VL extends ValidationListener> void validateMapInContext(ValidationContext<VL> context, ValidateCallback s) {
+        // jsr303 spec: For Map, the value of each Map.Entry is validated (key
+        // is not validated).
+        Map<?, ?> currentBean = (Map<?, ?>) context.getBean();
+        MetaBean metaBean = context.getMetaBean();
+        final DynamicMetaBean dyn = getDynamicMetaBean(context);
+        context.setCurrentKey(null);
+        try {
+            for (Map.Entry<?, ?> entry : currentBean.entrySet()) {
+                Object value = entry.getValue();
+                if (value == null) {
+                    continue;
+                }
+                context.setCurrentKey(entry.getKey());
+                if (dyn == null) {
+                    context.setBean(value);
+                } else {
+                    context.setBean(value, dyn.resolveMetaBean(value));
+                }
+                s.validate();
+            }
+        } finally {
+            context.moveUp(currentBean, metaBean);
+        }
+    }
+
+    /**
+     * @param <VL>
+     * @param context
+     *            The current validation context.
+     * @return the current {@link DynamicMetaBean} in context, or
+     *         <code>null</code> if the current meta bean is not dynamic.
+     */
+    static private <VL extends ValidationListener> DynamicMetaBean getDynamicMetaBean(ValidationContext<VL> context) {
+        return context.getMetaBean() instanceof DynamicMetaBean ? (DynamicMetaBean) context.getMetaBean() : null;
+    }
+
+
+    /**
+     * Validate a single bean only, no related beans will be validated.
+     */
+    static public <VL extends ValidationListener> void validateBean(ValidationContext<VL> context) {
+        // execute all property level validations
+        for (MetaProperty prop : context.getMetaBean().getProperties()) {
+            context.setMetaProperty(prop);
+            validateProperty(context);
+        }
+
+        // execute all bean level validations
+        context.setMetaProperty(null);
+        for (Validation validation : context.getMetaBean().getValidations()) {
+            validation.validate(context);
+        }
+    }
+
+
+    /**
+     * Validate a single property only. Performs all validations
+     * for this property.
+     */
+    static public <VL extends ValidationListener> void validateProperty(ValidationContext<VL> context) {
+        for (Validation validation : context.getMetaProperty().getValidations()) {
+            validation.validate(context);
+        }
+    }
+}
diff --git a/trunk/bval-core/src/test/java/org/apache/bval/ValidationResultsTest.java b/trunk/bval-core/src/test/java/org/apache/bval/ValidationResultsTest.java
new file mode 100644
index 0000000..e16f9de
--- /dev/null
+++ b/trunk/bval-core/src/test/java/org/apache/bval/ValidationResultsTest.java
@@ -0,0 +1,60 @@
+/*
+ *  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.bval;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.model.MetaProperty;
+
+/**
+ * ValidationResults Tester.
+ */
+public class ValidationResultsTest extends TestCase {
+    private ValidationResults results;
+
+    public ValidationResultsTest(String name) {
+        super(name);
+    }
+
+    public void setUp() throws Exception {
+        super.setUp();
+        results = new ValidationResults();
+    }
+
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testValidationResults() throws Exception {
+        assertTrue(results.isEmpty());
+        BeanValidationContext<ValidationResults> ctx = new BeanValidationContext<ValidationResults>(results);
+        ctx.setBean(this);
+        ctx.setMetaProperty(new MetaProperty());
+        ctx.getMetaProperty().setName("prop");
+        results.addError("test", ctx);
+        assertFalse(results.isEmpty());
+        assertTrue(results.hasErrorForReason("test"));
+        assertTrue(results.hasError(this, "prop"));
+        assertTrue(results.hasError(this, null));
+        assertFalse(results.hasError(this, "prop2"));
+    }
+
+    public static Test suite() {
+        return new TestSuite(ValidationResultsTest.class);
+    }
+}
diff --git a/trunk/bval-core/src/test/java/org/apache/bval/model/ExampleEnum.java b/trunk/bval-core/src/test/java/org/apache/bval/model/ExampleEnum.java
new file mode 100644
index 0000000..10f87a9
--- /dev/null
+++ b/trunk/bval-core/src/test/java/org/apache/bval/model/ExampleEnum.java
@@ -0,0 +1,27 @@
+/*
+ *  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.bval.model;
+
+/**
+ * Description: <br>
+ * User: roman.stumm<br>
+ * Date: 17.06.2010<br>
+ * Time: 18:00:12<br>
+ */
+public enum ExampleEnum {
+    VALUE1, VALUE2, VALUE3
+}
diff --git a/trunk/bval-core/src/test/java/org/apache/bval/model/MetaPropertyTest.java b/trunk/bval-core/src/test/java/org/apache/bval/model/MetaPropertyTest.java
new file mode 100644
index 0000000..35b626a
--- /dev/null
+++ b/trunk/bval-core/src/test/java/org/apache/bval/model/MetaPropertyTest.java
@@ -0,0 +1,51 @@
+/*
+ *  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.bval.model;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * MetaProperty Tester.
+ *
+ * @author <Authors name>
+ * @since <pre>02/12/2009</pre>
+ * @version 1.0
+ */
+public class MetaPropertyTest extends TestCase {
+    public MetaPropertyTest(String name) {
+        super(name);
+    }
+
+
+    public void testGetTypeClass() throws Exception {
+        MetaProperty prop = new MetaProperty();
+        prop.setType(String.class);
+        assertEquals(String.class, prop.getTypeClass());
+        assertEquals(String.class, prop.getType());
+        prop.setType(new DynaTypeEnum(ExampleEnum.class, ExampleEnum.VALUE1.name(),
+              ExampleEnum.VALUE3.name()));
+        assertEquals(ExampleEnum.class, prop.getTypeClass());
+        assertEquals(2, ((DynaTypeEnum)prop.getType()).getEnumConstants().length);
+    }
+
+
+    public static Test suite() {
+        return new TestSuite(MetaPropertyTest.class);
+    }
+}
diff --git a/trunk/bval-core/src/test/resources/log4j.xml b/trunk/bval-core/src/test/resources/log4j.xml
new file mode 100644
index 0000000..3c4bb67
--- /dev/null
+++ b/trunk/bval-core/src/test/resources/log4j.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<!-- ===================================================================== -->
+<!--                                                                       -->
+<!--  Log4j Configuration                                                  -->
+<!--                                                                       -->
+<!-- ===================================================================== -->
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+
+  <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+    <param name="Threshold" value="DEBUG"/>
+    <layout class="org.apache.log4j.PatternLayout">
+      <param name="ConversionPattern" value="%d %-5p %c - %m%n"/>
+    </layout>
+  </appender>
+
+  <root>
+    <appender-ref ref="CONSOLE"/>
+  </root>
+
+</log4j:configuration>
diff --git a/trunk/bval-extras/pom.xml b/trunk/bval-extras/pom.xml
new file mode 100644
index 0000000..bab695f
--- /dev/null
+++ b/trunk/bval-extras/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<!--
+	Maven release plugin requires the project tag to be on a single line.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.bval</groupId>
+        <artifactId>bval-parent</artifactId>
+        <version>0.4</version>
+    </parent>
+
+    <artifactId>bval-extras</artifactId>
+    <name>Apache BVal :: bval-extras (optional)</name>
+    <packaging>bundle</packaging>
+
+    <description>BVal - non-JSR303 routines and constraints</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.bval</groupId>
+            <artifactId>org.apache.bval.bundle</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <!--
+            default profile using geronimo-validation_1.0_spec.jar
+            active when property "ri" is not present.
+        -->
+        <profile>
+            <id>geronimo</id>
+            <activation>
+                <property>
+                    <name>!ri</name>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>org.apache.geronimo.specs</groupId>
+                    <artifactId>geronimo-validation_1.0_spec</artifactId>
+                    <!-- allow users to choose an API provider -->
+                    <scope>provided</scope>
+                </dependency>
+            </dependencies>
+        </profile>
+        <!--
+            optional profile using javax.validation/validation-api.jar
+            from RI manually active when property "-Pri" is provided.
+        -->
+        <profile>
+            <id>ri</id>
+            <activation>
+                <property>
+                    <name>ri</name>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>javax.validation</groupId>
+                    <artifactId>validation-api</artifactId>
+                    <!-- allow users to choose an API provider -->
+                    <scope>provided</scope>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+
+    <build>
+        <defaultGoal>install</defaultGoal>
+
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+            </resource>
+        </resources>
+    </build>
+
+</project>
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumber.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumber.java
new file mode 100644
index 0000000..7e48693
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumber.java
@@ -0,0 +1,54 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+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;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = ABANumberValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface ABANumber {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.checkdigit.ABANumber.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidator.java
new file mode 100644
index 0000000..4eed858
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidator.java
@@ -0,0 +1,70 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+/**
+ * Modulus 10 <b>ABA Number</b> (or <b>Routing Transit Number</b> (RTN)) Check Digit
+ * calculation/validation.
+ * <p>
+ * ABA Numbers (or Routing Transit Numbers) are a nine digit numeric code used
+ * to identify American financial institutions for things such as checks or deposits
+ * (ABA stands for the American Bankers Association).
+ * <p>
+ * Check digit calculation is based on <i>modulus 10</i> with digits being weighted
+ * based on their position (from right to left) as follows:
+ * <ul>
+ *     <li>Digits 1, 4 and & 7 are weighted 1
+ *     <li>Digits 2, 5 and & 8 are weighted 7
+ *     <li>Digits 3, 6 and & 9 are weighted 3
+ * </ul>
+ * <p>
+ * For further information see
+ *  <a href="http://en.wikipedia.org/wiki/Routing_transit_number">Wikipedia -
+ *  Routing transit number</a>.
+ */
+public final class ABANumberValidator
+    extends ModulusValidator<ABANumber> {
+
+    /** weighting given to digits depending on their right position */
+    private static final int[] POSITION_WEIGHT = new int[] {3, 1, 7};
+
+    public ABANumberValidator() {
+        super(10);
+    }
+
+    /**
+     * Calculates the <i>weighted</i> value of a character in the
+     * code at a specified position.
+     * <p>
+     * ABA Routing numbers are weighted in the following manner:
+     * <pre><code>
+     *     left position: 1  2  3  4  5  6  7  8  9
+     *            weight: 3  7  1  3  7  1  3  7  1
+     * </code></pre>
+     *
+     * {@inheritDoc}
+     */
+    @Override
+    protected int weightedValue( int charValue, int leftPos, int rightPos )
+            throws Exception {
+        int weight = POSITION_WEIGHT[rightPos % 3];
+        return (charValue * weight);
+    }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIP.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIP.java
new file mode 100644
index 0000000..860b7d3
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIP.java
@@ -0,0 +1,54 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+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;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = CUSIPValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface CUSIP {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.checkdigit.CUSIP.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidator.java
new file mode 100644
index 0000000..82e8c29
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidator.java
@@ -0,0 +1,86 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import static java.lang.Character.getNumericValue;
+
+/**
+ * Modulus 10 <b>CUSIP</b> (North American Securities)
+ * Check Digit calculation/validation.
+ * <p>
+ * CUSIP Numbers are 9 character alphanumeric codes used
+ * to identify North American Securities.
+ * <p>
+ * Check digit calculation uses the <i>Modulus 10 Double Add Double</i> technique
+ * with every second digit being weighted by 2. Alphabetic characters are
+ * converted to numbers by their position in the alphabet starting with A being 10.
+ * Weighted numbers greater than ten are treated as two separate numbers.
+ * <p>
+ *
+ * <p>
+ * See <a href="http://en.wikipedia.org/wiki/CUSIP">Wikipedia - CUSIP</a>
+ * for more details.
+ */
+public final class CUSIPValidator
+    extends ModulusValidator<CUSIP> {
+
+    /** weighting given to digits depending on their right position */
+    private static final int[] POSITION_WEIGHT = new int[] {2, 1};
+
+    public CUSIPValidator() {
+        super(10);
+    }
+
+    /**
+     * Calculates the <i>weighted</i> value of a character in the
+     * code at a specified position.
+     * <p>
+     * ABA Routing numbers are weighted in the following manner:
+     * <pre><code>
+     *     left position: 1  2  3  4  5  6  7  8  9
+     *            weight: 3  7  1  3  7  1  3  7  1
+     * </code></pre>
+     *
+     * {@inheritDoc}
+     */
+    @Override
+    protected int weightedValue( int charValue, int leftPos, int rightPos )
+            throws Exception {
+        int weight = POSITION_WEIGHT[rightPos % 2];
+        int weightedValue = (charValue * weight);
+        return sumDigits(weightedValue);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected int toInt(char character, int leftPos, int rightPos) {
+        int charValue = getNumericValue(character);
+        if (charValue < 0 || charValue > 35) {
+            throw new IllegalArgumentException("Invalid Character["
+                                               + leftPos
+                                               + "] = '"
+                                               + charValue
+                                               + "'");
+        }
+        return charValue;
+    }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13.java
new file mode 100644
index 0000000..d72af8f
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13.java
@@ -0,0 +1,54 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+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;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = EAN13Validator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface EAN13 {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.checkdigit.EAN13.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13Validator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13Validator.java
new file mode 100644
index 0000000..4270394
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/EAN13Validator.java
@@ -0,0 +1,69 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+/**
+ * Modulus 10 <b>EAN-13</b> / <b>UPC</b> / <b>ISBN-13</b> Check Digit
+ * calculation/validation.
+ * <p>
+ * Check digit calculation is based on <i>modulus 10</i> with digits in
+ * an <i>odd</i> position (from right to left) being weighted 1 and <i>even</i>
+ * position digits being weighted 3.
+ * <p>
+ * For further information see:
+ * <ul>
+ *   <li>EAN-13 - see
+ *       <a href="http://en.wikipedia.org/wiki/European_Article_Number">Wikipedia -
+ *       European Article Number</a>.</li>
+ *   <li>UPC - see
+ *       <a href="http://en.wikipedia.org/wiki/Universal_Product_Code">Wikipedia -
+ *       Universal Product Code</a>.</li>
+ *   <li>ISBN-13 - see
+ *       <a href="http://en.wikipedia.org/wiki/ISBN">Wikipedia - International
+ *       Standard Book Number (ISBN)</a>.</li>
+ * </ul>
+ */
+public final class EAN13Validator
+    extends ModulusValidator<EAN13> {
+
+    /** weighting given to digits depending on their right position */
+    private static final int[] POSITION_WEIGHT = new int[] {3, 1};
+
+    public EAN13Validator() {
+        super(10);
+    }
+
+    /**
+     * <p>Calculates the <i>weighted</i> value of a character in the
+     * code at a specified position.</p>
+     *
+     * <p>For EAN-13 (from right to left) <b>odd</b> digits are weighted
+     * with a factor of <b>one</b> and <b>even</b> digits with a factor
+     * of <b>three</b>.</p>
+     *
+     * {@inheritDoc}
+     */
+    @Override
+    protected int weightedValue( int charValue, int leftPos, int rightPos )
+            throws Exception {
+        int weight = POSITION_WEIGHT[rightPos % 2];
+        return (charValue * weight);
+    }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBAN.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBAN.java
new file mode 100644
index 0000000..c2f33c8
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBAN.java
@@ -0,0 +1,54 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+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;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = IBANValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface IBAN {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.checkdigit.IBAN.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBANValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBANValidator.java
new file mode 100644
index 0000000..e365ce1
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/IBANValidator.java
@@ -0,0 +1,81 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import static java.lang.Character.getNumericValue;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * <b>IBAN</b> (International Bank Account Number) Check Digit calculation/validation.
+ * <p>
+ * This rountine is based on the ISO 7064 Mod 97,10 check digit caluclation routine.
+ * <p>
+ * The two check digit characters in a IBAN number are the third and fourth characters
+ * in the code. For <i>check digit</i> calculation/validation the first four characters are moved
+ * to the end of the code.
+ *  So <code>CCDDnnnnnnn</code> becomes <code>nnnnnnnCCDD</code> (where
+ *  <code>CC</code> is the country code and <code>DD</code> is the check digit). For
+ *  check digit calcualtion the check digit value should be set to zero (i.e.
+ *  <code>CC00nnnnnnn</code> in this example.
+ * <p>
+ * For further information see
+ *  <a href="http://en.wikipedia.org/wiki/International_Bank_Account_Number">Wikipedia -
+ *  IBAN number</a>.
+ */
+public final class IBANValidator
+    implements ConstraintValidator<IBAN, String> {
+
+    private static final long MAX = 999999999;
+
+    private static final long MODULUS = 97;
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isValid(String code, ConstraintValidatorContext context) {
+        if (code.length() < 5) {
+            return false;
+        }
+
+        String reformattedCode = code.substring(4) + code.substring(0, 4);
+        long total = 0;
+        for (int i = 0; i < reformattedCode.length(); i++) {
+            int charValue = getNumericValue(reformattedCode.charAt(i));
+            if (charValue < 0 || charValue > 35) {
+                return false;
+            }
+            total = (charValue > 9 ? total * 100 : total * 10) + charValue;
+            if (total > MAX) {
+                total = (total % MODULUS);
+            }
+        }
+
+        return (total % MODULUS) == 1;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize( IBAN iban ) {
+        // not needed
+    }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10.java
new file mode 100644
index 0000000..31aab70
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10.java
@@ -0,0 +1,54 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+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;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = ISBN10Validator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface ISBN10 {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.checkdigit.ISBN10.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10Validator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10Validator.java
new file mode 100644
index 0000000..cf2c133
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ISBN10Validator.java
@@ -0,0 +1,81 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+/**
+ * Modulus 11 <b>ISBN-10</b> Check Digit calculation/validation.
+ * <p>
+ * ISBN-10 Numbers are a numeric code except for the last (check) digit
+ * which can have a value of "X".
+ * <p>
+ * Check digit calculation is based on <i>modulus 11</i> with digits being weighted
+ * based by their position, from right to left  with the first digit being weighted
+ * 1, the second 2 and so on. If the check digit is calculated as "10" it is converted
+ * to "X".
+ * <p>
+ * <b>N.B.</b> From 1st January 2007 the book industry will start to use a new 13 digit
+ * ISBN number (rather than this 10 digit ISBN number) which uses the EAN-13 / UPC
+ * (see {@link EAN13CheckDigit}) standard.
+ * <p>
+ * For further information see:
+ * <ul>
+ *   <li><a href="http://en.wikipedia.org/wiki/ISBN">Wikipedia - International
+ *       Standard Book Number (ISBN)</a>.</li>
+ *   <li><a href="http://www.isbn.org/standards/home/isbn/transition.asp">ISBN-13
+ *       Transition details</a>.</li>
+ * </ul>
+ */
+public final class ISBN10Validator
+    extends ModulusValidator<ISBN10> {
+
+    public ISBN10Validator() {
+        super(11);
+    }
+
+    /**
+     * Calculates the <i>weighted</i> value of a charcter in the
+     * code at a specified position.
+     *
+     * <p>For ISBN-10 (from right to left) digits are weighted
+     * by their position.</p>
+     *
+     * {@inheritDoc}
+     */
+    @Override
+    protected int weightedValue( int charValue, int leftPos, int rightPos )
+            throws Exception {
+        return (charValue * rightPos);
+    }
+
+    /**
+     * <p>Convert a character at a specified position to an
+     * integer value.</p>
+     *
+     * <p>Character 'X' check digit converted to 10.</p>
+     *
+     * {@inheritDoc}
+     */
+    protected int toInt(char character, int leftPos, int rightPos) {
+        if (rightPos == 1 && character == 'X') {
+            return 10;
+        }
+        return super.toInt(character, leftPos, rightPos);
+    }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Luhn.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Luhn.java
new file mode 100644
index 0000000..03be6ca
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Luhn.java
@@ -0,0 +1,54 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+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;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = LuhnValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Luhn {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.checkdigit.Luhn.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/LuhnValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/LuhnValidator.java
new file mode 100644
index 0000000..030fe3c
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/LuhnValidator.java
@@ -0,0 +1,65 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+/**
+ * Modulus 10 <b>Luhn</b> Check Digit calculation/validation.
+ * <p>
+ * Luhn check digits are used, for example, by:
+ * <ul>
+ *    <li><a href="http://en.wikipedia.org/wiki/Credit_card">Credit Card Numbers</a></li>
+ *    <li><a href="http://en.wikipedia.org/wiki/IMEI">IMEI Numbers</a> - International
+ *        Mobile Equipment Identity Numbers</li>
+ * </ul>
+ * Check digit calculation is based on <i>modulus 10</i> with digits in
+ * an <i>odd</i> position (from right to left) being weighted 1 and <i>even</i>
+ * position digits being weighted 2 (weighted values greater than 9 have 9 subtracted).
+ * <p>
+ * See <a href="http://en.wikipedia.org/wiki/Luhn_algorithm">Wikipedia</a>
+ * for more details.
+ */
+public final class LuhnValidator
+    extends ModulusValidator<Luhn> {
+
+    /** weighting given to digits depending on their right position */
+    private static final int[] POSITION_WEIGHT = new int[] {2, 1};
+
+    public LuhnValidator() {
+        super(10);
+    }
+
+    /**
+     * <p>Calculates the <i>weighted</i> value of a charcter in the
+     * code at a specified position.</p>
+     *
+     * <p>For Luhn (from right to left) <b>odd</b> digits are weighted
+     * with a factor of <b>one</b> and <b>even</b> digits with a factor
+     * of <b>two</b>. Weighted values > 9, have 9 subtracted</p>
+     *
+     * {@inheritDoc}
+     */
+    @Override
+    protected int weightedValue( int charValue, int leftPos, int rightPos )
+            throws Exception {
+        int weight = POSITION_WEIGHT[rightPos % 2];
+        int weightedValue = (charValue * weight);
+        return (weightedValue > 9 ? (weightedValue - 9) : weightedValue);
+    }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ModulusValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ModulusValidator.java
new file mode 100644
index 0000000..aeb7249
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/ModulusValidator.java
@@ -0,0 +1,140 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import static java.lang.Character.getNumericValue;
+import static java.lang.Character.isDigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Abstract <b>Modulus</b> Check digit calculation/validation.
+ * <p>
+ * Provides a <i>base</i> class for building <i>modulus</i> Check
+ * Digit routines.
+ * <p>
+ * This implementation only handles <i>numeric</i> codes, such as
+ * <b>EAN-13</b>. For <i>alphanumeric</i> codes such as <b>EAN-128</b> you
+ * will need to implement/override the <code>toInt()</code> and
+ * <code>toChar()</code> methods.
+ *
+ * @param <A>
+ */
+abstract class ModulusValidator<A extends Annotation>
+    implements ConstraintValidator<A, String> {
+
+    private final int modulus;
+
+    public ModulusValidator(int modulus) {
+        this.modulus = modulus;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public final void initialize(A annotation) {
+        // not needed ATM
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isValid(String code, ConstraintValidatorContext context) {
+        if (code.length() == 0) {
+            return false;
+        }
+        int total = 0;
+        for (int i = 0; i < code.length(); i++) {
+            int lth = code.length();
+            int leftPos  = i + 1;
+            int rightPos = lth - i;
+            try {
+                int charValue = toInt(code.charAt(i), leftPos, rightPos);
+                total += weightedValue(charValue, leftPos, rightPos);
+            } catch (Throwable e) {
+                return false;
+            }
+        }
+        if (total == 0) {
+            return false;
+        }
+        return (total % modulus) == 0;
+    }
+
+    /**
+     * Calculates the <i>weighted</i> value of a character in the
+     * code at a specified position.
+     * <p>
+     * Some modulus routines weight the value of a character
+     * depending on its position in the code (e.g. ISBN-10), while
+     * others use different weighting factors for odd/even positions
+     * (e.g. EAN or Luhn). Implement the appropriate mechanism
+     * required by overriding this method.
+     *
+     * @param charValue The numeric value of the character
+     * @param leftPos The position of the character in the code, counting from left to right
+     * @param rightPos The position of the character in the code, counting from right to left
+     * @return The weighted value of the character
+     */
+    protected abstract int weightedValue(int charValue, int leftPos, int rightPos)
+        throws Exception;
+
+    /**
+     * Convert a character at a specified position to an integer value.
+     * <p>
+     * <b>Note:</b> this implementation only handlers numeric values
+     * For non-numeric characters, override this method to provide
+     * character-->integer conversion.
+     *
+     * @param character The character to convert
+     * @param leftPos The position of the character in the code, counting from left to right
+     * @param rightPos The positionof the character in the code, counting from right to left
+     * @return The integer value of the character
+     */
+    protected int toInt(char character, int leftPos, int rightPos) {
+        if (isDigit(character)) {
+            return getNumericValue(character);
+        }
+        throw new IllegalArgumentException("Invalid Character["
+                                           + leftPos
+                                           + "] = '"
+                                           + character
+                                           + "'");
+    }
+
+    /**
+     * Add together the individual digits in a number.
+     *
+     * @param number The number whose digits are to be added
+     * @return The sum of the digits
+     */
+    protected static int sumDigits(int number) {
+        int total = 0;
+        int todo = number;
+        while (todo > 0) {
+            total += todo % 10;
+            todo  = todo / 10;
+        }
+        return total;
+    }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Sedol.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Sedol.java
new file mode 100644
index 0000000..2c90cb0
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Sedol.java
@@ -0,0 +1,54 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+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;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = SedolValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Sedol {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.checkdigit.Sedol.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/SedolValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/SedolValidator.java
new file mode 100644
index 0000000..4476084
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/SedolValidator.java
@@ -0,0 +1,75 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import static java.lang.Character.getNumericValue;
+
+/**
+ * Modulus 10 <b>SEDOL</b> (UK Securities) Check Digit calculation/validation.
+ * <p>
+ * SEDOL Numbers are 7 character alphanumeric codes used
+ * to identify UK Securities (SEDOL stands for Stock Exchange Daily Official List).
+ * <p>
+ * Check digit calculation is based on <i>modulus 10</i> with digits being weighted
+ * based on their position, from left to right, as follows:
+ * <p>
+ * <pre><code>
+ *      position:  1  2  3  4  5  6  7
+ *     weighting:  1  3  1  7  3  9  1
+ * </code></pre>
+ * <p>
+ * See <a href="http://en.wikipedia.org/wiki/SEDOL">Wikipedia - SEDOL</a>
+ * for more details.
+ */
+public final class SedolValidator
+    extends ModulusValidator<Sedol> {
+
+    /** weighting given to digits depending on their right position */
+    private static final int[] POSITION_WEIGHT = new int[] {1, 3, 1, 7, 3, 9, 1};
+
+    public SedolValidator() {
+        super(10);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected int weightedValue( int charValue, int leftPos, int rightPos )
+            throws Exception {
+        return (charValue * POSITION_WEIGHT[leftPos - 1]);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected int toInt( char character, int leftPos, int rightPos ) {
+        int charValue = getNumericValue(character);
+        if (charValue < 0 || charValue > 35) {
+            throw new IllegalArgumentException("Invalid Character["
+                                               + leftPos
+                                               + "] = '"
+                                               + charValue
+                                               + "'");
+        }
+        return charValue;
+    }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Verhoeff.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Verhoeff.java
new file mode 100644
index 0000000..abdf759
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/Verhoeff.java
@@ -0,0 +1,54 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+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;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = VerhoeffValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Verhoeff {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.checkdigit.Verhoeff.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffValidator.java
new file mode 100644
index 0000000..19a9594
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffValidator.java
@@ -0,0 +1,90 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import static java.lang.Character.getNumericValue;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * <b>Verhoeff</b> (Dihedral) Check Digit calculation/validation.
+ * <p>
+ * Check digit calculation for numeric codes using a
+ * <a href="http://en.wikipedia.org/wiki/Dihedral_group">Dihedral Group</a>
+ * of order 10.
+ * <p>
+ * See <a href="http://en.wikipedia.org/wiki/Verhoeff_algorithm">Wikipedia
+ *  - Verhoeff algorithm</a> for more details.
+ */
+public final class VerhoeffValidator
+    implements ConstraintValidator<Verhoeff, String> {
+
+    /** D - multiplication table */
+    private static final int[][] D_TABLE = new int[][] {
+        {0,  1,  2,  3,  4,  5,  6,  7,  8,  9},
+        {1,  2,  3,  4,  0,  6,  7,  8,  9,  5},
+        {2,  3,  4,  0,  1,  7,  8,  9,  5,  6},
+        {3,  4,  0,  1,  2,  8,  9,  5,  6,  7},
+        {4,  0,  1,  2,  3,  9,  5,  6,  7,  8},
+        {5,  9,  8,  7,  6,  0,  4,  3,  2,  1},
+        {6,  5,  9,  8,  7,  1,  0,  4,  3,  2},
+        {7,  6,  5,  9,  8,  2,  1,  0,  4,  3},
+        {8,  7,  6,  5,  9,  3,  2,  1,  0,  4},
+        {9,  8,  7,  6,  5,  4,  3,  2,  1,  0}};
+
+    /** P - permutation table */
+    private static final int[][] P_TABLE = new int[][] {
+        {0,  1,  2,  3,  4,  5,  6,  7,  8,  9},
+        {1,  5,  7,  6,  2,  8,  3,  0,  9,  4},
+        {5,  8,  0,  3,  7,  9,  6,  1,  4,  2},
+        {8,  9,  1,  6,  0,  4,  3,  5,  2,  7},
+        {9,  4,  5,  3,  1,  2,  6,  8,  7,  0},
+        {4,  2,  8,  6,  5,  7,  3,  9,  0,  1},
+        {2,  7,  9,  3,  8,  0,  6,  4,  1,  5},
+        {7,  0,  4,  6,  9,  1,  3,  2,  5,  8}};
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isValid(String code, ConstraintValidatorContext context) {
+        if (code.length() == 0) {
+            return false;
+        }
+
+        int checksum = 0;
+        for (int i = 0; i < code.length(); i++) {
+            int idx = code.length() - (i + 1);
+            int num = getNumericValue(code.charAt(idx));
+            if (num < 0 || num > 9) {
+                return false;
+            }
+            checksum = D_TABLE[checksum][P_TABLE[i % 8][num]];
+        }
+        return checksum == 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Verhoeff iban) {
+        // not needed
+    }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/package-info.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/package-info.java
new file mode 100644
index 0000000..a81b2ed
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/checkdigit/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * This package contains <i>Check Digit</i> validation routines.
+ */
+package org.apache.bval.extras.constraints.checkdigit;
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/AmericanExpress.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/AmericanExpress.java
new file mode 100644
index 0000000..4d340d3
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/AmericanExpress.java
@@ -0,0 +1,59 @@
+/*
+ * 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.bval.extras.constraints.creditcard;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+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;
+import javax.validation.constraints.Pattern;
+
+import org.apache.bval.extras.constraints.checkdigit.Luhn;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Luhn
+@Pattern(regexp="^(3[47]\\d{13})$")
+@Constraint(validatedBy={})
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface AmericanExpress {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.creditcard.AmericanExpress.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Diners.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Diners.java
new file mode 100644
index 0000000..3f21de8
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Diners.java
@@ -0,0 +1,59 @@
+/*
+ * 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.bval.extras.constraints.creditcard;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+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;
+import javax.validation.constraints.Pattern;
+
+import org.apache.bval.extras.constraints.checkdigit.Luhn;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Luhn
+@Pattern(regexp="^(30[0-5]\\d{11}|3095\\d{10}|36\\d{12}|3[8-9]\\d{12})$")
+@Constraint(validatedBy={})
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Diners {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.creditcard.Diners.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Discover.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Discover.java
new file mode 100644
index 0000000..56c3ca9
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Discover.java
@@ -0,0 +1,59 @@
+/*
+ * 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.bval.extras.constraints.creditcard;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+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;
+import javax.validation.constraints.Pattern;
+
+import org.apache.bval.extras.constraints.checkdigit.Luhn;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Luhn
+@Pattern(regexp="^((6011\\d{12})|(64[4-9]\\d{13})|(65\\d{14}))$")
+@Constraint(validatedBy={})
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Discover {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.creditcard.Discover.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Mastercard.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Mastercard.java
new file mode 100644
index 0000000..cb3a627
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Mastercard.java
@@ -0,0 +1,59 @@
+/*
+ * 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.bval.extras.constraints.creditcard;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+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;
+import javax.validation.constraints.Pattern;
+
+import org.apache.bval.extras.constraints.checkdigit.Luhn;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Luhn
+@Pattern(regexp="^(5[1-5]\\d{14})$")
+@Constraint(validatedBy={})
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Mastercard {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.creditcard.Mastercard.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Visa.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Visa.java
new file mode 100644
index 0000000..1ae223e
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/Visa.java
@@ -0,0 +1,59 @@
+/*
+ * 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.bval.extras.constraints.creditcard;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.PARAMETER;
+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;
+import javax.validation.constraints.Pattern;
+
+import org.apache.bval.extras.constraints.checkdigit.Luhn;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Luhn
+@Pattern(regexp="^(4)(\\d{12}|\\d{15})$")
+@Constraint(validatedBy={})
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Visa {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.creditcard.Visa.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/package-info.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/package-info.java
new file mode 100644
index 0000000..4d10d9e
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/creditcard/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * This package contains <i>Credit Card</i> validation routines.
+ */
+package org.apache.bval.extras.constraints.creditcard;
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/Directory.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/Directory.java
new file mode 100644
index 0000000..92abcb2
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/Directory.java
@@ -0,0 +1,51 @@
+/*
+ * 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.bval.extras.constraints.file;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = DirectoryValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Directory {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.file.Directory.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/DirectoryValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/DirectoryValidator.java
new file mode 100644
index 0000000..61eb8ed
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/DirectoryValidator.java
@@ -0,0 +1,46 @@
+/*
+ * 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.bval.extras.constraints.file;
+
+import java.io.File;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+
+/**
+ * Description: <br/>
+ */
+public class DirectoryValidator implements ConstraintValidator<Directory, File> {
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isValid(File value, ConstraintValidatorContext context) {
+        return value.exists() && value.isDirectory();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Directory parameters) {
+        // do nothing (as long as Directory has no properties)
+    }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/NotDirectory.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/NotDirectory.java
new file mode 100644
index 0000000..0f7deff
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/NotDirectory.java
@@ -0,0 +1,51 @@
+/*
+ * 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.bval.extras.constraints.file;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is not a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = DirectoryValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface NotDirectory {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.file.NotDirectory.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/NotDirectoryValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/NotDirectoryValidator.java
new file mode 100644
index 0000000..0b4ff97
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/NotDirectoryValidator.java
@@ -0,0 +1,46 @@
+/*
+ * 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.bval.extras.constraints.file;
+
+import java.io.File;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+
+/**
+ * Description: <br/>
+ */
+public class NotDirectoryValidator implements ConstraintValidator<NotDirectory, File> {
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isValid(File value, ConstraintValidatorContext context) {
+        return value.exists() && !value.isDirectory();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(NotDirectory parameters) {
+        // do nothing (as long as Directory has no properties)
+    }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/Symlink.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/Symlink.java
new file mode 100644
index 0000000..006f8cd
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/Symlink.java
@@ -0,0 +1,51 @@
+/*
+ * 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.bval.extras.constraints.file;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a symbolic link<br/>
+ */
+@Documented
+@Constraint( validatedBy = SymlinkValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Symlink {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.file.Symlink.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/SymlinkValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/SymlinkValidator.java
new file mode 100644
index 0000000..3b16b2d
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/SymlinkValidator.java
@@ -0,0 +1,75 @@
+/*
+ * 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.bval.extras.constraints.file;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+
+/**
+ * Description: <br/>
+ */
+public class SymlinkValidator implements ConstraintValidator<Symlink, File> {
+
+    /**
+     * The Windows separator character.
+     */
+    private static final char WINDOWS_SEPARATOR = '\\';
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isValid(File value, ConstraintValidatorContext context) {
+        if (!value.exists()) {
+            return false;
+        }
+
+        // routine kindly borrowed from Apache Commons-IO
+
+        if (File.separatorChar == WINDOWS_SEPARATOR) {
+            return false;
+        }
+
+        try {
+            File fileInCanonicalDir = null;
+            if (value.getParent() == null) {
+                fileInCanonicalDir = value;
+            } else {
+                File canonicalDir = value.getParentFile().getCanonicalFile();
+                fileInCanonicalDir = new File(canonicalDir, value.getName());
+            }
+
+            return (!fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile()));
+        } catch (IOException e) {
+            // TODO: is it true?
+            return false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Symlink parameters) {
+        // do nothing (as long as Symlink has no properties)
+    }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/package-info.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/package-info.java
new file mode 100644
index 0000000..b877ab0
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/file/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * {@link java.io.File} constraints validators.
+ */
+package org.apache.bval.extras.constraints.file;
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/Domain.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/Domain.java
new file mode 100644
index 0000000..9ff3eba
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/Domain.java
@@ -0,0 +1,53 @@
+/*
+ * 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.bval.extras.constraints.net;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = DomainValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface Domain {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.net.Domain.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+    boolean allowLocal() default false;
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/DomainValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/DomainValidator.java
new file mode 100644
index 0000000..52b3ef4
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/DomainValidator.java
@@ -0,0 +1,463 @@
+/*
+ * 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.bval.extras.constraints.net;
+
+import static java.util.Arrays.asList;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * <p><b>Domain name</b> validation routines.</p>
+ *
+ * <p>
+ * This validator provides methods for validating Internet domain names
+ * and top-level domains.
+ * </p>
+ *
+ * <p>Domain names are evaluated according
+ * to the standards <a href="http://www.ietf.org/rfc/rfc1034.txt">RFC1034</a>,
+ * section 3, and <a href="http://www.ietf.org/rfc/rfc1123.txt">RFC1123</a>,
+ * section 2.1. No accomodation is provided for the specialized needs of
+ * other applications; if the domain name has been URL-encoded, for example,
+ * validation will fail even though the equivalent plaintext version of the
+ * same name would have passed.
+ * </p>
+ *
+ * <p>
+ * Validation is also provided for top-level domains (TLDs) as defined and
+ * maintained by the Internet Assigned Numbers Authority (IANA):
+ * </p>
+ *
+ *   <ul>
+ *     <li>{@link #isValidInfrastructureTld} - validates infrastructure TLDs
+ *         (<code>.arpa</code>, etc.)</li>
+ *     <li>{@link #isValidGenericTld} - validates generic TLDs
+ *         (<code>.com, .org</code>, etc.)</li>
+ *     <li>{@link #isValidCountryCodeTld} - validates country code TLDs
+ *         (<code>.us, .uk, .cn</code>, etc.)</li>
+ *   </ul>
+ *
+ * <p>
+ * (<b>NOTE</b>: This class does not provide IP address lookup for domain names or
+ * methods to ensure that a given domain name matches a specific IP; see
+ * {@link java.net.InetAddress} for that functionality.)
+ * </p>
+ */
+public class DomainValidator implements ConstraintValidator<Domain, String> {
+
+    private boolean allowLocal;
+
+    // Regular expression strings for hostnames (derived from RFC2396 and RFC 1123)
+    private static final Pattern DOMAIN_LABEL = Pattern.compile("\\p{Alnum}(?>[\\p{Alnum}-]*\\p{Alnum})*");
+
+    private static final Pattern DOMAIN_NAME_REGEX = Pattern.compile("^(?:"
+                                                                     + DOMAIN_LABEL.pattern()
+                                                                     + "\\.)+(\\p{Alpha}{2,})$");
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isValid(String domain, ConstraintValidatorContext context) {
+        Matcher matcher = DOMAIN_NAME_REGEX.matcher(domain);
+        if (matcher.matches()) {
+            domain = matcher.group(1);
+            return isValidTld(domain);
+        } else if (allowLocal && DOMAIN_LABEL.matcher(domain).matches()) {
+           return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the specified <code>String</code> matches any
+     * IANA-defined top-level domain. Leading dots are ignored if present.
+     * The search is case-sensitive.
+     *
+     * @param tld the parameter to check for TLD status
+     * @return true if the parameter is a TLD
+     */
+    boolean isValidTld(String tld) {
+        if (allowLocal && isValidLocalTld(tld)) {
+           return true;
+        }
+
+        tld = chompLeadingDot(tld).toLowerCase();
+        return isValidInfrastructureTld(tld)
+                || isValidGenericTld(tld)
+                || isValidCountryCodeTld(tld);
+    }
+
+    /**
+     * Returns true if the specified <code>String</code> matches any
+     * IANA-defined infrastructure top-level domain. Leading dots are
+     * ignored if present. The search is case-sensitive.
+     *
+     * @param iTld the parameter to check for infrastructure TLD status
+     * @return true if the parameter is an infrastructure TLD
+     */
+    static boolean isValidInfrastructureTld(String iTld) {
+        return INFRASTRUCTURE_TLDS.contains(iTld);
+    }
+
+    /**
+     * Returns true if the specified <code>String</code> matches any
+     * IANA-defined generic top-level domain. Leading dots are ignored
+     * if present. The search is case-sensitive.
+     *
+     * @param gTld the parameter to check for generic TLD status
+     * @return true if the parameter is a generic TLD
+     */
+    static boolean isValidGenericTld(String gTld) {
+        return GENERIC_TLDS.contains(gTld);
+    }
+
+    /**
+     * Returns true if the specified <code>String</code> matches any
+     * IANA-defined country code top-level domain. Leading dots are
+     * ignored if present. The search is case-sensitive.
+     *
+     * @param ccTld the parameter to check for country code TLD status
+     * @return true if the parameter is a country code TLD
+     */
+    static boolean isValidCountryCodeTld(String ccTld) {
+        return COUNTRY_CODE_TLDS.contains(ccTld);
+    }
+
+    /**
+     * Returns true if the specified <code>String</code> matches any
+     * widely used "local" domains (localhost or localdomain). Leading dots are
+     * ignored if present. The search is case-sensitive.
+     *
+     * @param iTld the parameter to check for local TLD status
+     * @return true if the parameter is an local TLD
+     */
+    static boolean isValidLocalTld(String iTld) {
+        return LOCAL_TLDS.contains(iTld);
+    }
+
+    private static String chompLeadingDot(String str) {
+        if (str.startsWith(".")) {
+            return str.substring(1);
+        }
+        return str;
+    }
+
+    // ---------------------------------------------
+    // ----- TLDs defined by IANA
+    // ----- Authoritative and comprehensive list at:
+    // ----- http://data.iana.org/TLD/tlds-alpha-by-domain.txt
+
+    private static final List<String> INFRASTRUCTURE_TLDS = asList(
+        "arpa",               // internet infrastructure
+        "root"                // diagnostic marker for non-truncated root zone
+    );
+
+    private static final List<String> GENERIC_TLDS = asList(
+        "aero",               // air transport industry
+        "asia",               // Pan-Asia/Asia Pacific
+        "biz",                // businesses
+        "cat",                // Catalan linguistic/cultural community
+        "com",                // commercial enterprises
+        "coop",               // cooperative associations
+        "info",               // informational sites
+        "jobs",               // Human Resource managers
+        "mobi",               // mobile products and services
+        "museum",             // museums, surprisingly enough
+        "name",               // individuals' sites
+        "net",                // internet support infrastructure/business
+        "org",                // noncommercial organizations
+        "pro",                // credentialed professionals and entities
+        "tel",                // contact data for businesses and individuals
+        "travel",             // entities in the travel industry
+        "gov",                // United States Government
+        "edu",                // accredited postsecondary US education entities
+        "mil",                // United States Military
+        "int"                 // organizations established by international treaty
+    );
+
+    private static final List<String> COUNTRY_CODE_TLDS = asList(
+        "ac",                 // Ascension Island
+        "ad",                 // Andorra
+        "ae",                 // United Arab Emirates
+        "af",                 // Afghanistan
+        "ag",                 // Antigua and Barbuda
+        "ai",                 // Anguilla
+        "al",                 // Albania
+        "am",                 // Armenia
+        "an",                 // Netherlands Antilles
+        "ao",                 // Angola
+        "aq",                 // Antarctica
+        "ar",                 // Argentina
+        "as",                 // American Samoa
+        "at",                 // Austria
+        "au",                 // Australia (includes Ashmore and Cartier Islands and Coral Sea Islands)
+        "aw",                 // Aruba
+        "ax",                 // Åland
+        "az",                 // Azerbaijan
+        "ba",                 // Bosnia and Herzegovina
+        "bb",                 // Barbados
+        "bd",                 // Bangladesh
+        "be",                 // Belgium
+        "bf",                 // Burkina Faso
+        "bg",                 // Bulgaria
+        "bh",                 // Bahrain
+        "bi",                 // Burundi
+        "bj",                 // Benin
+        "bm",                 // Bermuda
+        "bn",                 // Brunei Darussalam
+        "bo",                 // Bolivia
+        "br",                 // Brazil
+        "bs",                 // Bahamas
+        "bt",                 // Bhutan
+        "bv",                 // Bouvet Island
+        "bw",                 // Botswana
+        "by",                 // Belarus
+        "bz",                 // Belize
+        "ca",                 // Canada
+        "cc",                 // Cocos (Keeling) Islands
+        "cd",                 // Democratic Republic of the Congo (formerly Zaire)
+        "cf",                 // Central African Republic
+        "cg",                 // Republic of the Congo
+        "ch",                 // Switzerland
+        "ci",                 // Côte d'Ivoire
+        "ck",                 // Cook Islands
+        "cl",                 // Chile
+        "cm",                 // Cameroon
+        "cn",                 // China, mainland
+        "co",                 // Colombia
+        "cr",                 // Costa Rica
+        "cu",                 // Cuba
+        "cv",                 // Cape Verde
+        "cx",                 // Christmas Island
+        "cy",                 // Cyprus
+        "cz",                 // Czech Republic
+        "de",                 // Germany
+        "dj",                 // Djibouti
+        "dk",                 // Denmark
+        "dm",                 // Dominica
+        "do",                 // Dominican Republic
+        "dz",                 // Algeria
+        "ec",                 // Ecuador
+        "ee",                 // Estonia
+        "eg",                 // Egypt
+        "er",                 // Eritrea
+        "es",                 // Spain
+        "et",                 // Ethiopia
+        "eu",                 // European Union
+        "fi",                 // Finland
+        "fj",                 // Fiji
+        "fk",                 // Falkland Islands
+        "fm",                 // Federated States of Micronesia
+        "fo",                 // Faroe Islands
+        "fr",                 // France
+        "ga",                 // Gabon
+        "gb",                 // Great Britain (United Kingdom)
+        "gd",                 // Grenada
+        "ge",                 // Georgia
+        "gf",                 // French Guiana
+        "gg",                 // Guernsey
+        "gh",                 // Ghana
+        "gi",                 // Gibraltar
+        "gl",                 // Greenland
+        "gm",                 // The Gambia
+        "gn",                 // Guinea
+        "gp",                 // Guadeloupe
+        "gq",                 // Equatorial Guinea
+        "gr",                 // Greece
+        "gs",                 // South Georgia and the South Sandwich Islands
+        "gt",                 // Guatemala
+        "gu",                 // Guam
+        "gw",                 // Guinea-Bissau
+        "gy",                 // Guyana
+        "hk",                 // Hong Kong
+        "hm",                 // Heard Island and McDonald Islands
+        "hn",                 // Honduras
+        "hr",                 // Croatia (Hrvatska)
+        "ht",                 // Haiti
+        "hu",                 // Hungary
+        "id",                 // Indonesia
+        "ie",                 // Ireland (Éire)
+        "il",                 // Israel
+        "im",                 // Isle of Man
+        "in",                 // India
+        "io",                 // British Indian Ocean Territory
+        "iq",                 // Iraq
+        "ir",                 // Iran
+        "is",                 // Iceland
+        "it",                 // Italy
+        "je",                 // Jersey
+        "jm",                 // Jamaica
+        "jo",                 // Jordan
+        "jp",                 // Japan
+        "ke",                 // Kenya
+        "kg",                 // Kyrgyzstan
+        "kh",                 // Cambodia (Khmer)
+        "ki",                 // Kiribati
+        "km",                 // Comoros
+        "kn",                 // Saint Kitts and Nevis
+        "kp",                 // North Korea
+        "kr",                 // South Korea
+        "kw",                 // Kuwait
+        "ky",                 // Cayman Islands
+        "kz",                 // Kazakhstan
+        "la",                 // Laos (currently being marketed as the official domain for Los Angeles)
+        "lb",                 // Lebanon
+        "lc",                 // Saint Lucia
+        "li",                 // Liechtenstein
+        "lk",                 // Sri Lanka
+        "lr",                 // Liberia
+        "ls",                 // Lesotho
+        "lt",                 // Lithuania
+        "lu",                 // Luxembourg
+        "lv",                 // Latvia
+        "ly",                 // Libya
+        "ma",                 // Morocco
+        "mc",                 // Monaco
+        "md",                 // Moldova
+        "me",                 // Montenegro
+        "mg",                 // Madagascar
+        "mh",                 // Marshall Islands
+        "mk",                 // Republic of Macedonia
+        "ml",                 // Mali
+        "mm",                 // Myanmar
+        "mn",                 // Mongolia
+        "mo",                 // Macau
+        "mp",                 // Northern Mariana Islands
+        "mq",                 // Martinique
+        "mr",                 // Mauritania
+        "ms",                 // Montserrat
+        "mt",                 // Malta
+        "mu",                 // Mauritius
+        "mv",                 // Maldives
+        "mw",                 // Malawi
+        "mx",                 // Mexico
+        "my",                 // Malaysia
+        "mz",                 // Mozambique
+        "na",                 // Namibia
+        "nc",                 // New Caledonia
+        "ne",                 // Niger
+        "nf",                 // Norfolk Island
+        "ng",                 // Nigeria
+        "ni",                 // Nicaragua
+        "nl",                 // Netherlands
+        "no",                 // Norway
+        "np",                 // Nepal
+        "nr",                 // Nauru
+        "nu",                 // Niue
+        "nz",                 // New Zealand
+        "om",                 // Oman
+        "pa",                 // Panama
+        "pe",                 // Peru
+        "pf",                 // French Polynesia With Clipperton Island
+        "pg",                 // Papua New Guinea
+        "ph",                 // Philippines
+        "pk",                 // Pakistan
+        "pl",                 // Poland
+        "pm",                 // Saint-Pierre and Miquelon
+        "pn",                 // Pitcairn Islands
+        "pr",                 // Puerto Rico
+        "ps",                 // Palestinian territories (PA-controlled West Bank and Gaza Strip)
+        "pt",                 // Portugal
+        "pw",                 // Palau
+        "py",                 // Paraguay
+        "qa",                 // Qatar
+        "re",                 // Réunion
+        "ro",                 // Romania
+        "rs",                 // Serbia
+        "ru",                 // Russia
+        "rw",                 // Rwanda
+        "sa",                 // Saudi Arabia
+        "sb",                 // Solomon Islands
+        "sc",                 // Seychelles
+        "sd",                 // Sudan
+        "se",                 // Sweden
+        "sg",                 // Singapore
+        "sh",                 // Saint Helena
+        "si",                 // Slovenia
+        "sj",                 // Svalbard and Jan Mayen Islands Not in use (Norwegian dependencies; see .no)
+        "sk",                 // Slovakia
+        "sl",                 // Sierra Leone
+        "sm",                 // San Marino
+        "sn",                 // Senegal
+        "so",                 // Somalia
+        "sr",                 // Suriname
+        "st",                 // São Tomé and Príncipe
+        "su",                 // Soviet Union (deprecated)
+        "sv",                 // El Salvador
+        "sy",                 // Syria
+        "sz",                 // Swaziland
+        "tc",                 // Turks and Caicos Islands
+        "td",                 // Chad
+        "tf",                 // French Southern and Antarctic Lands
+        "tg",                 // Togo
+        "th",                 // Thailand
+        "tj",                 // Tajikistan
+        "tk",                 // Tokelau
+        "tl",                 // East Timor (deprecated old code)
+        "tm",                 // Turkmenistan
+        "tn",                 // Tunisia
+        "to",                 // Tonga
+        "tp",                 // East Timor
+        "tr",                 // Turkey
+        "tt",                 // Trinidad and Tobago
+        "tv",                 // Tuvalu
+        "tw",                 // Taiwan, Republic of China
+        "tz",                 // Tanzania
+        "ua",                 // Ukraine
+        "ug",                 // Uganda
+        "uk",                 // United Kingdom
+        "um",                 // United States Minor Outlying Islands
+        "us",                 // United States of America
+        "uy",                 // Uruguay
+        "uz",                 // Uzbekistan
+        "va",                 // Vatican City State
+        "vc",                 // Saint Vincent and the Grenadines
+        "ve",                 // Venezuela
+        "vg",                 // British Virgin Islands
+        "vi",                 // U.S. Virgin Islands
+        "vn",                 // Vietnam
+        "vu",                 // Vanuatu
+        "wf",                 // Wallis and Futuna
+        "ws",                 // Samoa (formerly Western Samoa)
+        "ye",                 // Yemen
+        "yt",                 // Mayotte
+        "yu",                 // Serbia and Montenegro (originally Yugoslavia)
+        "za",                 // South Africa
+        "zm",                 // Zambia
+        "zw"                  // Zimbabwe
+    );
+
+    private static final List<String> LOCAL_TLDS = asList(
+       "localhost",           // RFC2606 defined
+       "localdomain"          // Also widely used as localhost.localdomain
+   );
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(Domain domain) {
+        allowLocal = domain.allowLocal();
+    }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddress.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddress.java
new file mode 100644
index 0000000..4dc3bc0
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddress.java
@@ -0,0 +1,51 @@
+/*
+ * 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.bval.extras.constraints.net;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate a java.io.File is a directory<br/>
+ */
+@Documented
+@Constraint( validatedBy = InetAddressValidator.class )
+@Target( { FIELD, ANNOTATION_TYPE, PARAMETER } )
+@Retention( RUNTIME )
+public @interface InetAddress {
+
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.extras.constraints.net.InetAddress.message}";
+
+    Class<? extends Payload>[] payload() default {};
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddressValidator.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddressValidator.java
new file mode 100644
index 0000000..f0896f1
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/InetAddressValidator.java
@@ -0,0 +1,56 @@
+/*
+ * 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.bval.extras.constraints.net;
+
+import java.util.regex.Pattern;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * <p><b>InetAddress</b> validation and conversion routines (<code>java.net.InetAddress</code>).</p>
+ *
+ * <p>This class provides methods to validate a candidate IP address.
+ */
+public class InetAddressValidator implements ConstraintValidator<InetAddress, String> {
+
+    private static final Pattern IPV4_PATTERN = Pattern.compile("^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+                                                                + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+                                                                + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+                                                                + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isValid(String value, ConstraintValidatorContext context) {
+        if (!IPV4_PATTERN.matcher(value).matches()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void initialize(InetAddress parameters) {
+        // do nothing (as long as InetAddress has no properties)
+    }
+
+}
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/package-info.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/package-info.java
new file mode 100644
index 0000000..d7e7550
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/net/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * net constraints validators.
+ */
+package org.apache.bval.extras.constraints.net;
diff --git a/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/package-info.java b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/package-info.java
new file mode 100644
index 0000000..02f423c
--- /dev/null
+++ b/trunk/bval-extras/src/main/java/org/apache/bval/extras/constraints/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Contains constraints that are NOT part of the Bean Validation specification
+ * and might disappear as soon as a final version of the specification contains
+ * similar functionalities.
+ */
+package org.apache.bval.extras.constraints;
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidatorTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidatorTest.java
new file mode 100644
index 0000000..56d5e62
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ABANumberValidatorTest.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.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+
+/**
+ * ABA Number Validator Test.
+ */
+public class ABANumberValidatorTest extends AbstractCheckDigitTest {
+
+    @Override
+    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+        return new ABANumberValidator();
+    }
+
+    @Override
+    protected String[] getValid() {
+        return new String[] {
+            "123456780",
+            "123123123",
+            "011000015",
+            "111000038",
+            "231381116",
+            "121181976"
+        };
+    }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/AbstractCheckDigitTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/AbstractCheckDigitTest.java
new file mode 100644
index 0000000..36b5198
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/AbstractCheckDigitTest.java
@@ -0,0 +1,190 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.validation.ConstraintValidator;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public abstract class AbstractCheckDigitTest {
+
+    /** Check digit routine being tested */
+    private int checkDigitLth;
+
+    /** Check digit routine being tested */
+    private ConstraintValidator<? extends Annotation, String> routine;
+
+    /** Array of valid code values */
+    private String[] valid;
+
+    /** Array of invalid code values */
+    private String[] invalid;
+
+    /** code value which sums to zero */
+    private String zeroSum;
+
+    /** Prefix for error messages */
+    private String missingMessage;
+
+    public int getCheckDigitLth() {
+        return 1;
+    }
+
+    protected abstract ConstraintValidator<? extends Annotation, String> getConstraint();
+
+    protected abstract String[] getValid();
+
+    protected String[] getInvalid() {
+        return new String[] {"12345678A"};
+    }
+
+    protected String getZeroSum() {
+        return "0000000000";
+    }
+
+    public String getMissingMessage() {
+        return "Code is missing";
+    }
+
+    @Before
+    public void setUp() {
+        checkDigitLth = getCheckDigitLth();
+        routine = getConstraint();
+        valid = getValid();
+        invalid = getInvalid();
+        zeroSum = getZeroSum();
+        missingMessage = getMissingMessage();
+    }
+
+    /**
+     * Tear Down - clears routine and valid codes.
+     */
+    @After
+    public void tearDown() {
+        valid = null;
+        routine = null;
+    }
+
+    /**
+     * Test isValid() for valid values.
+     */
+    @Test
+    public void testIsValidTrue() {
+        // test valid values
+        for (int i = 0; i < valid.length; i++) {
+            assertTrue("valid[" + i +"]: " + valid[i], routine.isValid(valid[i], null));
+        }
+    }
+
+    /**
+     * Test isValid() for invalid values.
+     */
+    @Test
+    public void testIsValidFalse() {
+        // test invalid code values
+        for (int i = 0; i < invalid.length; i++) {
+            assertFalse("invalid[" + i +"]: " + invalid[i], routine.isValid(invalid[i], null));
+        }
+
+        // test invalid check digit values
+        String[] invalidCheckDigits = createInvalidCodes(valid);
+        for (int i = 0; i < invalidCheckDigits.length; i++) {
+            assertFalse("invalid check digit[" + i +"]: " + invalidCheckDigits[i], routine.isValid(invalidCheckDigits[i], null));
+        }
+    }
+
+    /**
+     * Test missing code
+     */
+    @Test
+    public void testMissingCode() {
+        // isValid() zero length
+        assertFalse("isValid() Zero Length", routine.isValid("", null));
+    }
+
+    /**
+     * Test zero sum
+     */
+    @Test
+    public void testZeroSum() {
+        assertFalse("isValid() Zero Sum", routine.isValid(zeroSum, null));
+    }
+
+    /**
+     * Returns an array of codes with invalid check digits.
+     *
+     * @param codes Codes with valid check digits
+     * @return Codes with invalid check digits
+     */
+    protected String[] createInvalidCodes(String[] codes) {
+        List<String> list = new ArrayList<String>();
+
+        // create invalid check digit values
+        for (int i = 0; i < codes.length; i++) {
+            String code = removeCheckDigit(codes[i]);
+            String check  = checkDigit(codes[i]);
+            for (int j = 0; j < 10; j++) {
+                String curr =  "" + Character.forDigit(j, 10);
+                if (!curr.equals(check)) {
+                    list.add(code + curr);
+                }
+            }
+        }
+
+        return list.toArray(new String[list.size()]);
+    }
+
+    /**
+     * Returns a code with the Check Digit (i.e. last character) removed.
+     *
+     * @param code The code
+     * @return The code without the check digit
+     */
+    protected String removeCheckDigit(String code) {
+        if (code == null || code.length() <= checkDigitLth) {
+            return null;
+        }
+        return code.substring(0, code.length() - checkDigitLth);
+    }
+
+    /**
+     * Returns the check digit (i.e. last character) for a code.
+     *
+     * @param code The code
+     * @return The check digit
+     */
+    protected String checkDigit(String code) {
+        if (code == null || code.length() <= checkDigitLth) {
+            return "";
+        }
+        int start = code.length() - checkDigitLth;
+        return code.substring(start);
+    }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidatorTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidatorTest.java
new file mode 100644
index 0000000..d522626
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/CUSIPValidatorTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+
+/**
+ * CUSIP Check Digit Test.
+ */
+public class CUSIPValidatorTest extends AbstractCheckDigitTest {
+
+    @Override
+    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+        return new CUSIPValidator();
+    }
+
+    @Override
+    protected String[] getValid() {
+        return new String[] {
+            "037833100",
+            "931142103",
+            "837649128",
+            "392690QT3",
+            "594918104",
+            "86770G101",
+            "Y8295N109",
+            "G8572F100"
+        };
+    }
+
+    @Override
+    protected String[] getInvalid() {
+        return new String[] {"0378#3100"};
+    }
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/EAN13CheckDigitTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/EAN13CheckDigitTest.java
new file mode 100644
index 0000000..80885fe
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/EAN13CheckDigitTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+
+/**
+ * EAN-13 Check Digit Test.
+ */
+public class EAN13CheckDigitTest extends AbstractCheckDigitTest {
+
+    @Override
+    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+        return new EAN13Validator();
+    }
+
+    @Override
+    protected String[] getValid() {
+        return new String[] {
+            "9780072129519",
+            "9780764558313",
+            "4025515373438",
+            "0095673400332"
+        };
+    }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/IBANCheckDigitTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/IBANCheckDigitTest.java
new file mode 100644
index 0000000..36ed7d2
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/IBANCheckDigitTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.validation.ConstraintValidator;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * IVAN Check Digit Test.
+ */
+public class IBANCheckDigitTest extends AbstractCheckDigitTest {
+
+    @Override
+    public int getCheckDigitLth() {
+        return 2;
+    }
+
+    @Override
+    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+        return new IBANValidator();
+    }
+
+    @Override
+    protected String[] getValid() {
+        return new String[] {
+            "AD1200012030200359100100",
+            "AT611904300234573201",
+            "AT611904300234573201",
+            "BE68539007547034",
+            "BE62510007547061",
+            "CY17002001280000001200527600",
+            "CZ6508000000192000145399",
+            "DK5000400440116243",
+            "EE382200221020145685",
+            "FI2112345600000785",
+            "FR1420041010050500013M02606",
+            "DE89370400440532013000",
+            "GI75NWBK000000007099453",
+            "GR1601101250000000012300695",
+            "HU42117730161111101800000000",
+            "IS140159260076545510730339",
+            "IE29AIBK93115212345678",
+            "IT60X0542811101000000123456",
+            "LV80BANK0000435195001",
+            "LT121000011101001000",
+            "LU280019400644750000",
+            "NL91ABNA0417164300",
+            "NO9386011117947",
+            "PL27114020040000300201355387",
+            "PT50000201231234567890154",
+            "SK3112000000198742637541",
+            "SI56191000000123438",
+            "ES8023100001180000012345",
+            "SE3550000000054910000003",
+            "CH3900700115201849173",
+            "GB29NWBK60161331926819"
+        };
+    }
+
+    @Override
+    protected String[] getInvalid() {
+        return new String[] {"510007+47061BE63"};
+    }
+
+    @Override
+    protected String getZeroSum() {
+        return null;
+    }
+
+    @Override
+    public String getMissingMessage() {
+        return "Invalid Code length=0";
+    }
+
+    /**
+     * Test zero sum
+     */
+    @Override
+    @Test
+    @Ignore
+    public void testZeroSum() {
+        // ignore, don't run this test
+    }
+
+    @Override
+    protected String[] createInvalidCodes( String[] codes ) {
+        List<String> list = new ArrayList<String>();
+
+        // create invalid check digit values
+        for (int i = 0; i < codes.length; i++) {
+            String code = removeCheckDigit(codes[i]);
+            String check  = checkDigit(codes[i]);
+            for (int j = 0; j < 96; j++) {
+                String curr =  j > 9 ? "" + j : "0" + j;
+                if (!curr.equals(check)) {
+                    list.add(code.substring(0, 2) + curr + code.substring(4));
+                }
+            }
+        }
+
+        return list.toArray(new String[list.size()]);
+    }
+
+
+
+    /**
+     * Returns a code with the Check Digit (i.e. last character) removed.
+     *
+     * @param code The code
+     * @return The code without the check digit
+     */
+    protected String removeCheckDigit(String code) {
+        return code.substring(0, 2) + "00" + code.substring(4);
+    }
+
+    /**
+     * Returns the check digit (i.e. last character) for a code.
+     *
+     * @param code The code
+     * @return The check digit
+     */
+    protected String checkDigit(String code) {
+        if (code == null || code.length() <= getCheckDigitLth()) {
+            return "";
+        }
+       return code.substring(2, 4);
+    }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ISBN10CheckDigitTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ISBN10CheckDigitTest.java
new file mode 100644
index 0000000..111d866
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/ISBN10CheckDigitTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+
+/**
+ * ISBN-10 Check Digit Test.
+ */
+public class ISBN10CheckDigitTest extends AbstractCheckDigitTest {
+
+    @Override
+    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+        return new ISBN10Validator();
+    }
+
+    @Override
+    protected String[] getValid() {
+        return new String[] {
+            "1930110995",
+            "020163385X",
+            "1932394354",
+            "1590596277"
+        };
+    }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/LuhnCheckDigitTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/LuhnCheckDigitTest.java
new file mode 100644
index 0000000..85c8234
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/LuhnCheckDigitTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+
+/**
+ * Luhn Check Digit Test.
+ */
+public class LuhnCheckDigitTest extends AbstractCheckDigitTest {
+
+    private static final String VALID_VISA       = "4417123456789113";
+
+    private static final String VALID_SHORT_VISA = "4222222222222";
+
+    private static final String VALID_AMEX       = "378282246310005";
+
+    private static final String VALID_MASTERCARD = "5105105105105100";
+
+    private static final String VALID_DISCOVER   = "6011000990139424";
+
+    private static final String VALID_DINERS     = "30569309025904";
+
+    @Override
+    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+        return new LuhnValidator();
+    }
+
+    @Override
+    protected String[] getValid() {
+        return new String[] {
+            VALID_VISA,
+            VALID_SHORT_VISA,
+            VALID_AMEX,
+            VALID_MASTERCARD,
+            VALID_DISCOVER,
+            VALID_DINERS
+        };
+    }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/SedolCheckDigitTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/SedolCheckDigitTest.java
new file mode 100644
index 0000000..0ebde22
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/SedolCheckDigitTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+
+/**
+ * Sedol Check Digit Test.
+ */
+public class SedolCheckDigitTest extends AbstractCheckDigitTest {
+
+    @Override
+    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+        return new SedolValidator();
+    }
+
+    @Override
+    protected String[] getValid() {
+        return new String[] {
+            "0263494",
+            "0870612",
+            "B06LQ97",
+            "3437575",
+            "B07LF55",
+        };
+    }
+
+    @Override
+    protected String[] getInvalid() {
+        return new String[] {"123#567"};
+    }
+
+    @Override
+    protected String getZeroSum() {
+        return "0000000";
+    }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffCheckDigitTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffCheckDigitTest.java
new file mode 100644
index 0000000..2db2372
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/checkdigit/VerhoeffCheckDigitTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.bval.extras.constraints.checkdigit;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.ConstraintValidator;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Verhoeff Check Digit Test.
+ */
+public class VerhoeffCheckDigitTest extends AbstractCheckDigitTest {
+
+    @Override
+    protected ConstraintValidator<? extends Annotation, String> getConstraint() {
+        return new VerhoeffValidator();
+    }
+
+    @Override
+    protected String[] getValid() {
+        return new String[] {
+            "15",
+            "1428570",
+            "12345678902"
+        };
+    }
+
+    /**
+     * Test zero sum
+     */
+    @Override
+    @Test
+    @Ignore
+    public void testZeroSum() {
+        // ignore, don't run this test
+    }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/DomainValidatorTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/DomainValidatorTest.java
new file mode 100644
index 0000000..fcf32e4
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/DomainValidatorTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.bval.extras.constraints.net;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.annotation.Annotation;
+
+import javax.validation.Payload;
+
+import org.apache.bval.extras.constraints.net.Domain;
+import org.apache.bval.extras.constraints.net.DomainValidator;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the DomainValidator.
+ */
+public class DomainValidatorTest {
+
+    private DomainValidator validator;
+
+    @Before
+    public void setUp() {
+        validator = new DomainValidator();
+    }
+
+    @Test
+    public void testValidDomains() {
+        assertTrue("apache.org should validate", validator.isValid("apache.org", null));
+        assertTrue("www.google.com should validate", validator.isValid("www.google.com", null));
+
+        assertTrue("test-domain.com should validate", validator.isValid("test-domain.com", null));
+        assertTrue("test---domain.com should validate", validator.isValid("test---domain.com", null));
+        assertTrue("test-d-o-m-ain.com should validate", validator.isValid("test-d-o-m-ain.com", null));
+        assertTrue("two-letter domain label should validate", validator.isValid("as.uk", null));
+
+        assertTrue("case-insensitive ApAchE.Org should validate", validator.isValid("ApAchE.Org", null));
+
+        assertTrue("single-character domain label should validate", validator.isValid("z.com", null));
+
+        assertTrue("i.have.an-example.domain.name should validate", validator.isValid("i.have.an-example.domain.name", null));
+    }
+
+    @Test
+    public void testInvalidDomains() {
+        assertFalse("bare TLD .org shouldn't validate", validator.isValid(".org", null));
+        assertFalse("domain name with spaces shouldn't validate", validator.isValid(" apache.org ", null));
+        assertFalse("domain name containing spaces shouldn't validate", validator.isValid("apa che.org", null));
+        assertFalse("domain name starting with dash shouldn't validate", validator.isValid("-testdomain.name", null));
+        assertFalse("domain name ending with dash shouldn't validate", validator.isValid("testdomain-.name", null));
+        assertFalse("domain name starting with multiple dashes shouldn't validate", validator.isValid("---c.com", null));
+        assertFalse("domain name ending with multiple dashes shouldn't validate", validator.isValid("c--.com", null));
+        assertFalse("domain name with invalid TLD shouldn't validate", validator.isValid("apache.rog", null));
+
+        assertFalse("URL shouldn't validate", validator.isValid("http://www.apache.org", null));
+        assertFalse("Empty string shouldn't validate as domain name", validator.isValid(" ", null));
+    }
+
+    @Test
+    public void testTopLevelDomains() {
+        // infrastructure TLDs
+        assertTrue(".arpa should validate as iTLD", DomainValidator.isValidInfrastructureTld("arpa"));
+        assertFalse(".com shouldn't validate as iTLD", DomainValidator.isValidInfrastructureTld("com"));
+
+        // generic TLDs
+        assertTrue(".name should validate as gTLD", DomainValidator.isValidGenericTld("name"));
+        assertFalse(".us shouldn't validate as gTLD", DomainValidator.isValidGenericTld("us"));
+
+        // country code TLDs
+        assertTrue(".uk should validate as ccTLD", DomainValidator.isValidCountryCodeTld("uk"));
+        assertFalse(".org shouldn't validate as ccTLD", DomainValidator.isValidCountryCodeTld("org"));
+
+        // case-insensitive
+        assertTrue(".COM should validate as TLD", validator.isValidTld("COM"));
+        assertTrue(".BiZ should validate as TLD", validator.isValidTld("BiZ"));
+
+        // corner cases
+        assertFalse("invalid TLD shouldn't validate", validator.isValid("nope", null));
+        assertFalse("empty string shouldn't validate as TLD", validator.isValid("", null));
+    }
+
+    @Test
+    public void testAllowLocal() {
+       DomainValidator noLocal = new DomainValidator();
+       DomainValidator allowLocal = new DomainValidator();
+       allowLocal.initialize( new Domain()
+       {
+
+            public Class<? extends Annotation> annotationType() {
+                // not needed
+                return null;
+            }
+
+            public Class<? extends Payload>[] payload() {
+                // not needed
+                return null;
+            }
+
+            public String message() {
+                // not needed
+                return null;
+            }
+
+            public Class<?>[] groups() {
+                // not needed
+                return null;
+            }
+
+            public boolean allowLocal() {
+                // enable the local
+                return true;
+            }
+        });
+
+       // Default won't allow local
+       assertFalse("localhost.localdomain should validate", noLocal.isValid("localhost.localdomain", null));
+       assertFalse("localhost should validate", noLocal.isValid("localhost", null));
+
+       // But it may be requested
+       assertTrue("localhost.localdomain should validate", allowLocal.isValid("localhost.localdomain", null));
+       assertTrue("localhost should validate", allowLocal.isValid("localhost", null));
+       assertTrue("hostname should validate", allowLocal.isValid("hostname", null));
+       assertTrue("machinename should validate", allowLocal.isValid("machinename", null));
+
+       // Check the localhost one with a few others
+       assertTrue("apache.org should validate", allowLocal.isValid("apache.org", null));
+       assertFalse("domain name with spaces shouldn't validate", allowLocal.isValid(" apache.org ", null));
+    }
+
+    @Test
+    public void testIDN() {
+       assertTrue("b\u00fccher.ch in IDN should validate", validator.isValid("www.xn--bcher-kva.ch", null));
+    }
+
+}
diff --git a/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/InetAddressValidatorTest.java b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/InetAddressValidatorTest.java
new file mode 100644
index 0000000..4703279
--- /dev/null
+++ b/trunk/bval-extras/src/test/java/org/apache/bval/extras/constraints/net/InetAddressValidatorTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.bval.extras.constraints.net;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.bval.extras.constraints.net.InetAddressValidator;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test cases for InetAddressValidator.
+ *
+ * @version $Revision$
+ */
+public class InetAddressValidatorTest {
+
+    private InetAddressValidator validator;
+
+    @Before
+    public void setUp() {
+        validator = new InetAddressValidator();
+    }
+
+    /**
+     * Test IPs that point to real, well-known hosts (without actually looking them up).
+     */
+    @Test
+    public void testInetAddressesFromTheWild() {
+        assertTrue("www.apache.org IP should be valid",       validator.isValid("140.211.11.130", null));
+        assertTrue("www.l.google.com IP should be valid",     validator.isValid("72.14.253.103", null));
+        assertTrue("fsf.org IP should be valid",              validator.isValid("199.232.41.5", null));
+        assertTrue("appscs.ign.com IP should be valid",       validator.isValid("216.35.123.87", null));
+    }
+
+    /**
+     * Test valid and invalid IPs from each address class.
+     */
+    @Test
+    public void testInetAddressesByClass() {
+        assertTrue("class A IP should be valid",              validator.isValid("24.25.231.12", null));
+        assertFalse("illegal class A IP should be invalid",   validator.isValid("2.41.32.324", null));
+
+        assertTrue("class B IP should be valid",              validator.isValid("135.14.44.12", null));
+        assertFalse("illegal class B IP should be invalid",   validator.isValid("154.123.441.123", null));
+
+        assertTrue("class C IP should be valid",              validator.isValid("213.25.224.32", null));
+        assertFalse("illegal class C IP should be invalid",   validator.isValid("201.543.23.11", null));
+
+        assertTrue("class D IP should be valid",              validator.isValid("229.35.159.6", null));
+        assertFalse("illegal class D IP should be invalid",   validator.isValid("231.54.11.987", null));
+
+        assertTrue("class E IP should be valid",              validator.isValid("248.85.24.92", null));
+        assertFalse("illegal class E IP should be invalid",   validator.isValid("250.21.323.48", null));
+    }
+
+    /**
+     * Test reserved IPs.
+     */
+    @Test
+    public void testReservedInetAddresses() {
+        assertTrue("localhost IP should be valid",            validator.isValid("127.0.0.1", null));
+        assertTrue("broadcast IP should be valid",            validator.isValid("255.255.255.255", null));
+    }
+
+    /**
+     * Test obviously broken IPs.
+     */
+    @Test
+    public void testBrokenInetAddresses() {
+        assertFalse("IP with characters should be invalid",   validator.isValid("124.14.32.abc", null));
+        assertFalse("IP with three groups should be invalid", validator.isValid("23.64.12", null));
+        assertFalse("IP with five groups should be invalid",  validator.isValid("26.34.23.77.234", null));
+    }
+
+}
diff --git a/trunk/bval-guice/pom.xml b/trunk/bval-guice/pom.xml
new file mode 100644
index 0000000..a5bd492
--- /dev/null
+++ b/trunk/bval-guice/pom.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<!--
+	Maven release plugin requires the project tag to be on a single line.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.bval</groupId>
+        <artifactId>bval-parent</artifactId>
+        <version>0.4</version>
+    </parent>
+
+    <artifactId>bval-guice</artifactId>
+    <name>Apache BVal :: bval-guice (optional)</name>
+    <packaging>bundle</packaging>
+
+    <description>BVal - Google Guice Integration</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.bval</groupId>
+            <artifactId>org.apache.bval.bundle</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.inject</groupId>
+            <artifactId>javax.inject</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.inject</groupId>
+            <artifactId>guice</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>aopalliance</groupId>
+            <artifactId>aopalliance</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <!--
+            default profile using geronimo-validation_1.0_spec.jar
+            active when property "ri" is not present.
+        -->
+        <profile>
+            <id>geronimo</id>
+            <activation>
+                <property>
+                    <name>!ri</name>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>org.apache.geronimo.specs</groupId>
+                    <artifactId>geronimo-validation_1.0_spec</artifactId>
+                    <!-- allow users to choose an API provider -->
+                    <scope>provided</scope>
+                </dependency>
+            </dependencies>
+        </profile>
+        <!--
+            optional profile using javax.validation/validation-api.jar
+            from RI manually active when property "-Pri" is provided.
+        -->
+        <profile>
+            <id>ri</id>
+            <activation>
+                <property>
+                    <name>ri</name>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>javax.validation</groupId>
+                    <artifactId>validation-api</artifactId>
+                    <!-- allow users to choose an API provider -->
+                    <scope>provided</scope>
+                </dependency>
+            </dependencies>
+        </profile>
+    </profiles>
+
+    <build>
+        <defaultGoal>install</defaultGoal>
+
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+            </resource>
+        </resources>
+    </build>
+
+</project>
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/ConfigurationStateProvider.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ConfigurationStateProvider.java
new file mode 100644
index 0000000..09c4db8
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ConfigurationStateProvider.java
@@ -0,0 +1,83 @@
+/*
+ * 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.bval.guice;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+import javax.validation.spi.BootstrapState;
+import javax.validation.spi.ConfigurationState;
+import javax.validation.spi.ValidationProvider;
+
+import org.apache.bval.jsr303.ConfigurationImpl;
+
+/**
+ * The {@code javax.validation.spi.ConfigurationState} provider implementation.
+ *
+ * @version $Id$
+ */
+public final class ConfigurationStateProvider implements Provider<ConfigurationState> {
+
+    @com.google.inject.Inject(optional = true)
+    private BootstrapState bootstrapState;
+
+    @Inject
+    private ValidationProvider<?> validationProvider;
+
+    @Inject
+    private TraversableResolver traversableResolver;
+
+    @Inject
+    private MessageInterpolator messageInterpolator;
+
+    @Inject
+    private ConstraintValidatorFactory constraintValidatorFactory;
+
+    public void setBootstrapState(BootstrapState bootstrapState) {
+        this.bootstrapState = bootstrapState;
+    }
+
+    public void setValidationProvider(ValidationProvider<?> validationProvider) {
+        this.validationProvider = validationProvider;
+    }
+
+    public void setTraversableResolver(TraversableResolver traversableResolver) {
+        this.traversableResolver = traversableResolver;
+    }
+
+    public void setMessageInterpolator(MessageInterpolator messageInterpolator) {
+        this.messageInterpolator = messageInterpolator;
+    }
+
+    public void setConstraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) {
+        this.constraintValidatorFactory = constraintValidatorFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConfigurationState get() {
+        ConfigurationImpl configuration = new ConfigurationImpl(this.bootstrapState, this.validationProvider);
+        configuration.traversableResolver(this.traversableResolver);
+        configuration.messageInterpolator(this.messageInterpolator);
+        configuration.constraintValidatorFactory(this.constraintValidatorFactory);
+        return configuration;
+    }
+
+}
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/GuiceAwareConstraintValidatorFactory.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/GuiceAwareConstraintValidatorFactory.java
new file mode 100644
index 0000000..4917b74
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/GuiceAwareConstraintValidatorFactory.java
@@ -0,0 +1,49 @@
+/*
+ * 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.bval.guice;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorFactory;
+
+import com.google.inject.Injector;
+
+/**
+ * {@code javax.validation.ConstraintValidatorFactory} implementation that
+ * relies on Google Guice.
+ *
+ * @version $Id$
+ */
+@Singleton
+final class GuiceAwareConstraintValidatorFactory implements ConstraintValidatorFactory {
+
+    @Inject
+    private Injector injector;
+
+    public void setInjector(Injector injector) {
+        this.injector = injector;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
+        return this.injector.getInstance(key);
+    }
+
+}
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/Validate.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/Validate.java
new file mode 100644
index 0000000..d1ca823
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/Validate.java
@@ -0,0 +1,72 @@
+/*
+ * 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.bval.guice;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import javax.validation.ConstraintViolationException;
+
+import com.google.inject.BindingAnnotation;
+
+/**
+ * Marker for methods which arguments have to be validated.
+ *
+ * @version $Id$
+ */
+@BindingAnnotation
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD })
+public @interface Validate {
+
+    /**
+     * The groups have to be validated, empty by default.
+     *
+     * @return the groups have to be validated, empty by default.
+     */
+    Class<?>[] groups() default {};
+
+    /**
+     * Marks if the returned object by the intercepted method execution has to
+     * be validated, false by default.
+     *
+     * @return false by default.
+     */
+    boolean validateReturnedValue() default false;
+
+    /**
+     * The exception re-thrown when an error occurs during the validation.
+     *
+     * @return the exception re-thrown when an error occurs during the
+     *         validation.
+     */
+    Class<? extends Throwable> rethrowExceptionsAs() default ConstraintViolationException.class;
+
+    /**
+     * A custom error message when throwing the custom exception.
+     *
+     * It supports java.util.Formatter place holders, intercepted method
+     * arguments will be used as message format arguments.
+     *
+     * @return a custom error message when throwing the custom exception.
+     * @see java.util.Formatter#format(String, Object...)
+     */
+    String exceptionMessage() default "";
+
+}
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidateMethodInterceptor.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidateMethodInterceptor.java
new file mode 100644
index 0000000..55ff481
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidateMethodInterceptor.java
@@ -0,0 +1,181 @@
+/*
+ * 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.bval.guice;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.bval.jsr303.extensions.MethodValidator;
+
+/**
+ * Method interceptor for {@link Validate} annotation.
+ *
+ * @version $Id$
+ */
+public final class ValidateMethodInterceptor implements MethodInterceptor {
+
+    private static final Class<?>[] CAUSE_TYPES = new Class[]{ Throwable.class };
+
+    private static final Class<?>[] MESSAGE_CAUSE_TYPES = new Class[]{ String.class, Throwable.class };
+
+    /**
+     * The {@link ValidatorFactory} reference.
+     */
+    @Inject
+    private ValidatorFactory validatorFactory;
+
+    /**
+     * Sets the {@link ValidatorFactory} reference.
+     *
+     * @param validatorFactory the {@link ValidatorFactory} reference
+     */
+    public void setValidatorFactory(ValidatorFactory validatorFactory) {
+        this.validatorFactory = validatorFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object invoke(MethodInvocation invocation) throws Throwable {
+        Validate validate = invocation.getMethod().getAnnotation(Validate.class);
+
+        Validator validator = this.validatorFactory.getValidator();
+        MethodValidator methodValidator = validator.unwrap(MethodValidator.class);
+
+        Set<ConstraintViolation<?>> constraintViolations = new HashSet<ConstraintViolation<?>>();
+        Class<?> clazz = invocation.getMethod().getDeclaringClass();
+        Method method = invocation.getMethod();
+        Object[] arguments = invocation.getArguments();
+        Class<?>[] groups = validate.groups();
+
+        constraintViolations.addAll(methodValidator.validateParameters(clazz,
+                method,
+                arguments,
+                groups));
+
+        if (!constraintViolations.isEmpty()) {
+            throw getException(new ConstraintViolationException(
+                    String.format("Validation error when calling method '%s' with arguments %s",
+                            method,
+                            Arrays.deepToString(arguments)),
+                    constraintViolations),
+                    validate.rethrowExceptionsAs(),
+                    validate.exceptionMessage(),
+                    arguments);
+        }
+
+        Object returnedValue = invocation.proceed();
+
+        if (validate.validateReturnedValue()) {
+            constraintViolations.addAll(methodValidator.validateReturnedValue(clazz, method, returnedValue, groups));
+
+            if (!constraintViolations.isEmpty()) {
+                throw getException(new ConstraintViolationException(
+                        String.format("Method '%s' returned a not valid value %s",
+                                method,
+                                returnedValue),
+                        constraintViolations),
+                        validate.rethrowExceptionsAs(),
+                        validate.exceptionMessage(),
+                        arguments);
+            }
+        }
+
+        return returnedValue;
+    }
+
+    /**
+     * Define the {@link Throwable} has to be thrown when a validation error
+     * occurs and the user defined the custom error wrapper.
+     *
+     * @param exception the occurred validation error.
+     * @param exceptionWrapperClass the user defined custom error wrapper.
+     * @return the {@link Throwable} has o be thrown.
+     */
+    private static Throwable getException(ConstraintViolationException exception,
+            Class<? extends Throwable> exceptionWrapperClass,
+            String exceptionMessage,
+            Object[] arguments) {
+        // check the thrown exception is of same re-throw type
+        if (exceptionWrapperClass == ConstraintViolationException.class) {
+            return exception;
+        }
+
+        // re-throw the exception as new exception
+        Throwable rethrowEx = null;
+
+        String errorMessage;
+        Object[] initargs;
+        Class<?>[] initargsType;
+
+        if (exceptionMessage.length() != 0) {
+            errorMessage = String.format(exceptionMessage, arguments);
+            initargs = new Object[]{ errorMessage, exception };
+            initargsType = MESSAGE_CAUSE_TYPES;
+        } else {
+            initargs = new Object[]{ exception };
+            initargsType = CAUSE_TYPES;
+        }
+
+        Constructor<? extends Throwable> exceptionConstructor = getMatchingConstructor(exceptionWrapperClass, initargsType);
+        if (exceptionConstructor != null) {
+            try {
+                rethrowEx = exceptionConstructor.newInstance(initargs);
+            } catch (Exception e) {
+                errorMessage = String.format("Impossible to re-throw '%s', it needs the constructor with %s argument(s).",
+                        exceptionWrapperClass.getName(),
+                        Arrays.toString(initargsType));
+                rethrowEx = new RuntimeException(errorMessage, e);
+            }
+        } else {
+            errorMessage = String.format("Impossible to re-throw '%s', it needs the constructor with %s or %s argument(s).",
+                    exceptionWrapperClass.getName(),
+                    Arrays.toString(CAUSE_TYPES),
+                    Arrays.toString(MESSAGE_CAUSE_TYPES));
+            rethrowEx = new RuntimeException(errorMessage);
+        }
+
+        return rethrowEx;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <E extends Throwable> Constructor<E> getMatchingConstructor(Class<E> type,
+            Class<?>[] argumentsType) {
+        Class<? super E> currentType = type;
+        while (Object.class != currentType) {
+            for (Constructor<?> constructor : currentType.getConstructors()) {
+                if (Arrays.equals(argumentsType, constructor.getParameterTypes())) {
+                    return (Constructor<E>) constructor;
+                }
+            }
+            currentType = currentType.getSuperclass();
+        }
+        return null;
+    }
+
+}
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidationModule.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidationModule.java
new file mode 100644
index 0000000..6ddbd6e
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidationModule.java
@@ -0,0 +1,64 @@
+/*
+ * 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.bval.guice;
+
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.spi.ConfigurationState;
+import javax.validation.spi.ValidationProvider;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.apache.bval.jsr303.ApacheValidationProvider;
+import org.apache.bval.jsr303.DefaultMessageInterpolator;
+import org.apache.bval.jsr303.resolver.DefaultTraversableResolver;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Scopes;
+import com.google.inject.TypeLiteral;
+import com.google.inject.matcher.Matchers;
+
+/**
+ * The Google-Guice module for Apache BVal.
+ *
+ * @version $Id$
+ */
+public final class ValidationModule extends AbstractModule {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void configure() {
+        // apache bval bootstrap
+        this.bind(MessageInterpolator.class).to(DefaultMessageInterpolator.class).in(Scopes.SINGLETON);
+        this.bind(TraversableResolver.class).to(DefaultTraversableResolver.class).in(Scopes.SINGLETON);
+        this.bind(ConstraintValidatorFactory.class).to(GuiceAwareConstraintValidatorFactory.class);
+        this.bind(new TypeLiteral<ValidationProvider<?>>() {}).to(ApacheValidationProvider.class).in(Scopes.SINGLETON);
+        this.bind(ConfigurationState.class).toProvider(ConfigurationStateProvider.class).in(Scopes.SINGLETON);
+        this.bind(ValidatorFactory.class).toProvider(ValidatorFactoryProvider.class).in(Scopes.SINGLETON);
+        this.bind(Validator.class).toProvider(ValidatorProvider.class);
+
+        // AOP stuff
+        MethodInterceptor validateMethodInterceptor = new ValidateMethodInterceptor();
+        this.binder().requestInjection(validateMethodInterceptor);
+        this.bindInterceptor(Matchers.any(), Matchers.annotatedWith(Validate.class), validateMethodInterceptor);
+    }
+
+}
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidatorFactoryProvider.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidatorFactoryProvider.java
new file mode 100644
index 0000000..7d9d477
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidatorFactoryProvider.java
@@ -0,0 +1,49 @@
+/*
+ * 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.bval.guice;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import javax.validation.ValidatorFactory;
+import javax.validation.spi.ConfigurationState;
+
+import org.apache.bval.jsr303.ApacheValidationProvider;
+
+/**
+ * Validator Factory guice provider implementation.
+ *
+ * @version $Id$
+ */
+@Singleton
+final class ValidatorFactoryProvider implements Provider<ValidatorFactory> {
+
+    @Inject
+    private ConfigurationState configurationState;
+
+    public void setConfigurationState(ConfigurationState configurationState) {
+        this.configurationState = configurationState;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ValidatorFactory get() {
+        return new ApacheValidationProvider().buildValidatorFactory(this.configurationState);
+    }
+
+}
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidatorProvider.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidatorProvider.java
new file mode 100644
index 0000000..06262dd
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/ValidatorProvider.java
@@ -0,0 +1,50 @@
+/*
+ * 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.bval.guice;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+/**
+ * Validator guice provider implementation.
+ *
+ * @version $Id$
+ */
+@Singleton
+final class ValidatorProvider implements Provider<Validator> {
+
+    /**
+     * The validator reference.
+     */
+    @Inject
+    private ValidatorFactory validatorFactory;
+
+    public void setValidatorFactory(ValidatorFactory validatorFactory) {
+        this.validatorFactory = validatorFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Validator get() {
+        return this.validatorFactory.getValidator();
+    }
+
+}
diff --git a/trunk/bval-guice/src/main/java/org/apache/bval/guice/package-info.java b/trunk/bval-guice/src/main/java/org/apache/bval/guice/package-info.java
new file mode 100644
index 0000000..b48dc05
--- /dev/null
+++ b/trunk/bval-guice/src/main/java/org/apache/bval/guice/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Contains Apache BVal support for Google-Guice.
+ *
+ * @version $Id$
+ */
+package org.apache.bval.guice;
diff --git a/trunk/bval-guice/src/test/java/org/apache/bval/guice/Country.java b/trunk/bval-guice/src/test/java/org/apache/bval/guice/Country.java
new file mode 100644
index 0000000..d35b182
--- /dev/null
+++ b/trunk/bval-guice/src/test/java/org/apache/bval/guice/Country.java
@@ -0,0 +1,77 @@
+/*
+ * 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.bval.guice;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+/**
+ * 
+ *
+ * @version $Id$
+ */
+public class Country {
+
+    @NotNull(groups = { Update.class })
+    private Long id;
+
+    @NotNull(groups = { Insert.class })
+    private String name;
+
+    @NotNull(groups = { Insert.class })
+    @Size(max = 2, groups = { Insert.class, Update.class })
+    private String iso2Code;
+
+    @NotNull(groups = { Insert.class })
+    @Size(max = 3, groups = { Insert.class, Update.class })
+    private String iso3Code;
+
+    public Long getId() {
+        return this.id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getIso2Code() {
+        return iso2Code;
+    }
+
+    public void setIso2Code(String iso2Code) {
+        this.iso2Code = iso2Code;
+    }
+
+    public String getIso3Code() {
+        return iso3Code;
+    }
+
+    public void setIso3Code(String iso3Code) {
+        this.iso3Code = iso3Code;
+    }
+
+}
\ No newline at end of file
diff --git a/trunk/bval-guice/src/test/java/org/apache/bval/guice/DummyCountryDao.java b/trunk/bval-guice/src/test/java/org/apache/bval/guice/DummyCountryDao.java
new file mode 100644
index 0000000..acc317e
--- /dev/null
+++ b/trunk/bval-guice/src/test/java/org/apache/bval/guice/DummyCountryDao.java
@@ -0,0 +1,55 @@
+/*
+ * 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.bval.guice;
+
+import javax.inject.Singleton;
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+/**
+ * 
+ *
+ * @version $Id$
+ */
+@Singleton
+public class DummyCountryDao {
+
+    @Validate(
+        groups = { Insert.class },
+        validateReturnedValue = true
+    )
+    public Country insertCountry(@NotNull(groups = { Insert.class }) String name,
+            @NotNull(groups = { Insert.class }) @Size(max = 2, groups = { Insert.class, Update.class }) String iso2Code,
+            @NotNull(groups = { Insert.class }) @Size(max = 3, groups = { Insert.class, Update.class }) String iso3Code) {
+        Country country = new Country();
+        country.setName(name);
+        country.setIso2Code(iso2Code);
+        country.setIso3Code(iso3Code);
+        return country;
+    }
+
+    @Validate(
+        groups = { Update.class },
+        rethrowExceptionsAs = DummyException.class,
+        exceptionMessage = "This is just a dummy message %s"
+    )
+    public int updateCountry(@Valid Country country) {
+        return 0;
+    }
+
+}
diff --git a/trunk/bval-guice/src/test/java/org/apache/bval/guice/DummyException.java b/trunk/bval-guice/src/test/java/org/apache/bval/guice/DummyException.java
new file mode 100644
index 0000000..764ca71
--- /dev/null
+++ b/trunk/bval-guice/src/test/java/org/apache/bval/guice/DummyException.java
@@ -0,0 +1,36 @@
+/*
+ * 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.bval.guice;
+
+/**
+ * 
+ *
+ * @version $Id$
+ */
+public final class DummyException extends Exception {
+
+    private static final long serialVersionUID = 1L;
+
+    public DummyException(Throwable cause) {
+        super(cause);
+    }
+
+    public DummyException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/trunk/bval-guice/src/test/java/org/apache/bval/guice/GuiceAwareValidationTestCase.java b/trunk/bval-guice/src/test/java/org/apache/bval/guice/GuiceAwareValidationTestCase.java
new file mode 100644
index 0000000..427bb92
--- /dev/null
+++ b/trunk/bval-guice/src/test/java/org/apache/bval/guice/GuiceAwareValidationTestCase.java
@@ -0,0 +1,95 @@
+/*
+ * 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.bval.guice;
+
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.validation.Validator;
+
+import junit.framework.TestCase;
+
+import com.google.inject.Guice;
+
+/**
+ * 
+ *
+ * @version $Id$
+ */
+public final class GuiceAwareValidationTestCase extends TestCase {
+
+    @Inject
+    private Validator validator;
+
+    @Inject
+    private DummyCountryDao dummyCountryDao;
+
+    public void setValidator(Validator validator) {
+        this.validator = validator;
+    }
+
+    public void setDummyCountryDao(DummyCountryDao dummyCountryDao) {
+        this.dummyCountryDao = dummyCountryDao;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        Guice.createInjector(new ValidationModule()).injectMembers(this);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        this.validator = null;
+        this.dummyCountryDao = null;
+    }
+
+    public void testInjectedValidation() {
+        Country country = new Country();
+        country.setName("Italy");
+        country.setIso2Code("it");
+        country.setIso3Code("ita");
+
+        Set<ConstraintViolation<Country>> violations = this.validator.validate(country);
+        assertTrue(violations.isEmpty());
+    }
+
+    public void testAOPInjectedValidation() {
+        this.dummyCountryDao.insertCountry("Italy", "it", "ita");
+    }
+
+    public void testAOPInjectedFailedValidation() {
+        try {
+            this.dummyCountryDao.insertCountry("Italy", "ita", "ita");
+            fail("javax.validation.ConstraintViolationException expected");
+        } catch (ConstraintViolationException cve) {
+            // do nothing
+        }
+    }
+
+    public void testRethrowWrappedException() {
+        try {
+            this.dummyCountryDao.updateCountry(new Country());
+            fail("org.apache.bval.guice.DummyException expected");
+        } catch (Exception e) {
+            assertEquals(DummyException.class, e.getClass());
+            assertTrue(e.getMessage().startsWith("This is just a dummy message "));
+        }
+    }
+
+}
diff --git a/trunk/bval-guice/src/test/java/org/apache/bval/guice/Insert.java b/trunk/bval-guice/src/test/java/org/apache/bval/guice/Insert.java
new file mode 100644
index 0000000..9384691
--- /dev/null
+++ b/trunk/bval-guice/src/test/java/org/apache/bval/guice/Insert.java
@@ -0,0 +1,28 @@
+/*
+ * 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.bval.guice;
+
+/**
+ * 
+ *
+ * @version $Id$
+ */
+public interface Insert {
+
+}
diff --git a/trunk/bval-guice/src/test/java/org/apache/bval/guice/Update.java b/trunk/bval-guice/src/test/java/org/apache/bval/guice/Update.java
new file mode 100644
index 0000000..e3fdecb
--- /dev/null
+++ b/trunk/bval-guice/src/test/java/org/apache/bval/guice/Update.java
@@ -0,0 +1,28 @@
+/*
+ * 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.bval.guice;
+
+/**
+ * 
+ *
+ * @version $Id$
+ */
+public interface Update {
+
+}
diff --git a/trunk/bval-json/pom.xml b/trunk/bval-json/pom.xml
new file mode 100644
index 0000000..492bc89
--- /dev/null
+++ b/trunk/bval-json/pom.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<!--
+	Maven release plugin requires the project tag to be on a single line.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.bval</groupId>
+        <artifactId>bval-parent</artifactId>
+        <version>0.4</version>
+    </parent>
+
+    <artifactId>bval-json</artifactId>
+    <name>Apache BVal :: bval-json (optional)</name>
+    <packaging>bundle</packaging>
+
+    <description>BVal - Optional JSON Component</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.bval</groupId>
+            <artifactId>bval-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.bval</groupId>
+            <artifactId>bval-xstream</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.freemarker</groupId>
+            <artifactId>freemarker</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.bval</groupId>
+            <artifactId>bval-core</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.bval</groupId>
+            <artifactId>bval-xstream</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <defaultGoal>install</defaultGoal>
+
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+            </resource>
+        </resources>
+
+    </build>
+</project>
+
diff --git a/trunk/bval-json/src/main/appended-resources/META-INF/NOTICE.vm b/trunk/bval-json/src/main/appended-resources/META-INF/NOTICE.vm
new file mode 100644
index 0000000..9a0c1ee
--- /dev/null
+++ b/trunk/bval-json/src/main/appended-resources/META-INF/NOTICE.vm
@@ -0,0 +1,25 @@
+##
+## 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.
+##
+
+The following copyright notice(s) were affixed to portions of this code
+with which this file is now or was at one time distributed.
+
+This product includes software developed by agimatec GmbH.
+Copyright 2007-2010 Agimatec GmbH. All rights reserved.
+
diff --git a/trunk/bval-json/src/main/java/org/apache/bval/json/JSONGenerator.java b/trunk/bval-json/src/main/java/org/apache/bval/json/JSONGenerator.java
new file mode 100644
index 0000000..dae9099
--- /dev/null
+++ b/trunk/bval-json/src/main/java/org/apache/bval/json/JSONGenerator.java
@@ -0,0 +1,79 @@
+/*
+ *  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.bval.json;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.bval.model.MetaBean;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+
+/**
+ * Description: Generate a JSON String for a collection of {@link MetaBean}s.
+ * This implementation uses a freemarker template to generate the output.<br/>
+ *
+ * This is an optional module which requires the freemarker dependency
+ * and template resourece file "bean-infos-json.ftl" with it.
+ */
+public class JSONGenerator {
+    private final Template template;
+
+    public JSONGenerator() throws IOException {
+        this("bean-infos-json.ftl");
+    }
+
+    public JSONGenerator(String templateName) throws IOException {
+        Configuration freemarker = new Configuration();
+        freemarker.setNumberFormat("0.######");  // prevent locale-sensitive number format
+        freemarker.setClassForTemplateLoading(getClass(), "");
+        template = freemarker.getTemplate(templateName);
+    }
+
+    public JSONGenerator(Template template) {
+        this.template = template;
+    }
+
+    public String toJSON(MetaBean metaBean) throws IOException, TemplateException {
+        List<MetaBean> metaBeans = new ArrayList<MetaBean>(1);
+        metaBeans.add(metaBean);
+        return toJSON(metaBeans);
+    }
+
+    public String toJSON(Collection<MetaBean> metaBeans)
+            throws IOException, TemplateException {
+        final StringWriter out = new StringWriter();
+        toJSON(metaBeans, out);
+        return out.toString();
+    }
+
+    public void toJSON(Collection<MetaBean> metaBeans, Writer out)
+            throws IOException, TemplateException {
+        Map<String, Object> rootMap = new HashMap<String, Object>();
+        rootMap.put("metaBeans", metaBeans);
+        rootMap.put("generator", this);
+        template.process(rootMap, out);
+    }
+}
diff --git a/trunk/bval-json/src/main/resources/org/apache/bval/json/bean-infos-json.ftl b/trunk/bval-json/src/main/resources/org/apache/bval/json/bean-infos-json.ftl
new file mode 100644
index 0000000..63012ad
--- /dev/null
+++ b/trunk/bval-json/src/main/resources/org/apache/bval/json/bean-infos-json.ftl
@@ -0,0 +1,85 @@
+bval.namespace("bval.metadata");

+

+<#-- 

+ 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.

+-->

+

+

+(function(){

+<#assign var = 0>

+<#assign varrefs = {}>

+

+<#list metaBeans as metaBean>

+<#assign varrefs = varrefs + {metaBean.id : var}>

+var metaBean${var} = {

+ "id" : "${metaBean.id}",

+ <#if metaBean.beanClass??>"beanClass" : "${metaBean.beanClass.name}",</#if>

+ <#if metaBean.name??>"name" : "${metaBean.name}",</#if>

+ "features" :{<#rt/>

+   <#list metaBean.features?keys as featureKey>

+      <#assign value = metaBean.features[featureKey]>

+   "${featureKey}" : <#if

+          value?is_string>"${value}"<#elseif

+          value?is_boolean>${value?string}<#elseif

+          value?is_number>${value}<#elseif

+          value?is_date>${value?string.full}<#elseif

+          !(value??)>null<#elseif value?is_sequence>[<#list value as v>"${v}"<#if v_has_next>,</#if></#list>]<#elseif

+          value?is_hash>{<#list value?keys as key>"${key}" : "${value[key]}"<#if key_has_next>,</#if></#list>}<#else

+          >"?"</#if><#rt/><#if

+          featureKey_has_next>,</#if>

+   </#list>

+   <#lt/>},

+ "properties" :{

+   <#list metaBean.properties as property>

+     "${property.name}":{

+       "name" : "${property.name}",

+       <#if property.type??>"type" : "${property.type}",</#if>

+       <#if property.typeClass??>"typeClass" : "${property.typeClass.name}",</#if>

+       "features" : {<#if property.type?? &&

+       property.type.enum!false>"enum" : {<#list property.type.enumConstants as enum>"${enum.name()}": "${enum.name()}"<#if enum_has_next>, </#if></#list>}<#if property.features?size &gt; 0>,</#if></#if><#list

+       property.features?keys as featureKey>

+       <#assign value = property.features[featureKey]>

+       "${featureKey}" : <#rt/><#if

+          value?is_string>"${value}"<#elseif

+          value?is_boolean>${value?string}<#elseif

+          value?is_number>${value}<#elseif

+          value?is_date>${value?string.full}<#elseif

+          !(value??)>null<#elseif value?is_sequence>[<#list value as v>"${v}"<#if v_has_next>,</#if></#list>]<#elseif

+          value?is_hash_ex>{<#list value?keys as key>"${key}" : "${value[key]}"<#if key_has_next>,</#if></#list>}<#else

+          >"?"</#if><#rt/><#if featureKey_has_next>,</#if>

+       </#list>

+       }

+     }<#if property_has_next>,</#if>

+   </#list>

+   }

+};

+<#assign var = var + 1>

+</#list>

+

+<#assign var = 0>

+<#list metaBeans as metaBean><#list

+    metaBean.properties as property><#if

+    property.metaBean?? && varrefs[property.metaBean.id]??>

+metaBean${var}.properties.${property.name}.metaBean = metaBean${varrefs[property.metaBean.id]};

+</#if></#list><#assign var = var + 1></#list><#assign var = 0>

+bval.metadata.metaBeans = {

+<#list metaBeans as metaBean>

+       "${metaBean.id}" : metaBean${var}<#if metaBean_has_next>,</#if>

+       <#assign var = var + 1>

+</#list>};

+})();

diff --git a/trunk/bval-json/src/test/java/org/apache/bval/json/JSONGeneratorTest.java b/trunk/bval-json/src/test/java/org/apache/bval/json/JSONGeneratorTest.java
new file mode 100644
index 0000000..c69cf39
--- /dev/null
+++ b/trunk/bval-json/src/test/java/org/apache/bval/json/JSONGeneratorTest.java
@@ -0,0 +1,104 @@
+/*
+ *  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.bval.json;
+
+import junit.framework.TestCase;
+import org.apache.bval.example.BusinessEnum;
+import org.apache.bval.example.BusinessObject;
+import org.apache.bval.example.BusinessObjectAddress;
+import org.apache.bval.model.DynaTypeEnum;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.xml.XMLMetaBeanManager;
+import org.apache.bval.xml.XMLMetaBeanURLLoader;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Description: <br>
+ * Author: roman.stumm<br>
+ */
+public class JSONGeneratorTest extends TestCase {
+    private XMLMetaBeanManager mbm;
+
+    public JSONGeneratorTest(String name) {
+        super(name);
+    }
+
+    public void setUp() throws Exception {
+        super.setUp();
+        mbm = new XMLMetaBeanManager();
+        mbm.addLoader(new XMLMetaBeanURLLoader(BusinessObject.class.getResource("test-beanInfos.xml")));
+    }
+
+    public void testBeanInfosCustomPatchGenerated() throws Exception {
+        MetaBean mbean = mbm.findForClass(BusinessObject.class);
+        MetaProperty mprop = mbean.getProperty("lastName");
+        assertTrue(mprop.isMandatory());
+
+        mbm.getCache().removeFromCache(mbean);
+        mbm.addLoader(new XMLMetaBeanURLLoader(BusinessObject.class.getResource("test-beanInfos-custom.xml")));
+        mbean = mbm.findForClass(BusinessObject.class);
+        mprop = mbean.getProperty("lastName");
+        assertTrue(!mprop.isMandatory());
+
+        JSONGenerator converter = new JSONGenerator();
+
+        List<MetaBean> metaBeans = new ArrayList<MetaBean>(2);
+        metaBeans.add(mbean);
+        MetaBean mbean2 = mbm.findForId("UnknownObject");
+        metaBeans.add(mbean2);
+        String json = converter.toJSON(metaBeans);
+        assertNotNull(json);
+        // System.out.println(json);
+    }
+
+    public void testJSON() throws Exception {
+        MetaBean info = mbm.findForClass(BusinessObject.class);
+        MetaBean info2 = info.getProperty("address").getMetaBean();
+
+        // empty default bean without xml backup
+        MetaBean info3 = mbm.findForClass(BusinessObjectAddress.class);
+        JSONGenerator converter = new JSONGenerator();
+
+        List<MetaBean> metaBeans = new ArrayList<MetaBean>(2);
+        metaBeans.add(info);
+        metaBeans.add(info2);
+        metaBeans.add(info3);
+        String json = converter.toJSON(metaBeans);
+        assertNotNull(json);
+        // System.out.println(json);
+    }
+
+    public void testJSON_dynaTypeEnum() throws Exception {
+        MetaBean info = mbm.findForClass(BusinessObject.class);
+        MetaProperty choice = info.getProperty("choice");
+        choice.setType(new DynaTypeEnum(BusinessEnum.class, "CUSTOM_1", "CUSTOM_2"));
+
+        JSONGenerator converter = new JSONGenerator();
+
+        List<MetaBean> metaBeans = new ArrayList<MetaBean>(1);
+        metaBeans.add(info);
+        String json = converter.toJSON(metaBeans);
+        assertNotNull(json);
+        // System.out.println(json);
+        assertTrue(json.indexOf("CUSTOM_1") > 0);
+        assertTrue(json.indexOf("CUSTOM_2") > 0);
+        assertTrue(json.indexOf("VALUE1") < 0);
+    }
+}
diff --git a/trunk/bval-jsr303/pom.xml b/trunk/bval-jsr303/pom.xml
new file mode 100644
index 0000000..e20d121
--- /dev/null
+++ b/trunk/bval-jsr303/pom.xml
@@ -0,0 +1,226 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<!--
+	Maven release plugin requires the project tag to be on a single line.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.bval</groupId>
+        <artifactId>bval-parent</artifactId>
+        <version>0.4</version>
+    </parent>
+
+    <artifactId>bval-jsr303</artifactId>
+    <name>Apache BVal :: bval-jsr303</name>
+    <packaging>jar</packaging>
+
+    <description>Implementation specific classes for JSR 303 Bean Validation 1.0</description>
+
+    <profiles>
+        <profile>
+            <id>jaxb</id>
+            <activation>
+                <jdk>1.5</jdk>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>javax.xml.bind</groupId>
+                    <artifactId>jaxb-api</artifactId>
+                    <version>2.1</version>
+                </dependency>
+                <dependency>
+                    <groupId>com.sun.xml.bind</groupId>
+                    <artifactId>jaxb-impl</artifactId>
+                    <version>2.1.3</version>
+                </dependency>
+            </dependencies>
+        </profile>
+        <!--
+            default profile using geronimo-validation_1.0_spec.jar active when
+            property "ri" is not present.
+        -->
+        <profile>
+            <id>geronimo</id>
+            <activation>
+                <property>
+                    <name>!ri</name>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>org.apache.geronimo.specs</groupId>
+                    <artifactId>geronimo-validation_1.0_spec</artifactId>
+                    <!-- allow users to choose an API provider -->
+                    <scope>provided</scope>
+                </dependency>
+            </dependencies>
+        </profile>
+        <!--
+            optional profile using javax.validation/validation-api.jar from RI
+            manually active when property "-Dri" is provided.
+        -->
+        <profile>
+            <id>ri</id>
+            <activation>
+                <property>
+                    <name>ri</name>
+                </property>
+            </activation>
+            <dependencies>
+                <dependency>
+                    <groupId>javax.validation</groupId>
+                    <artifactId>validation-api</artifactId>
+                    <!-- allow users to choose an API provider -->
+                    <scope>provided</scope>
+                </dependency>
+            </dependencies>
+        </profile>
+        <profile>
+            <id>sec</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <includes>
+                                <include>**/*Test.java</include>
+                                <include>**/*TestCase.java</include>
+                            </includes>
+                            <argLine>-Djava.security.manager -Djava.security.policy=${project.basedir}/src/test/resources/java.policy</argLine>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.bval</groupId>
+            <artifactId>bval-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.2</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- optional dependencies -->
+        <dependency>
+            <groupId>org.apache.bval</groupId>
+            <artifactId>bval-xstream</artifactId>
+            <version>${project.version}</version>
+            <!-- don't pull into OSGi bundle -->
+            <scope>provided</scope>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-jpa_2.0_spec</artifactId>
+            <!-- allow users to choose an API provider -->
+            <scope>provided</scope>
+            <optional>true</optional>
+        </dependency>
+        <!-- Testing dependencies -->
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>jcl-over-slf4j</artifactId>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-simple</artifactId>
+                <scope>test</scope>
+            </dependency>
+    </dependencies>
+
+    <build>
+        <defaultGoal>install</defaultGoal>
+
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+            </resource>
+            <resource>
+                <directory>src/main/xsd</directory>
+                <targetPath>META-INF</targetPath>
+            </resource>
+        </resources>
+
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>jaxb2-maven-plugin</artifactId>
+                <version>1.2</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>xjc</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <packageName>org.apache.bval.jsr303.xml</packageName>
+                    <extension>true</extension>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>test-jar</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                        <inherited>false</inherited>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <!-- 2.12 has http://jira.codehaus.org/browse/SUREFIRE-836 -->
+                <version>2.11</version>
+                <configuration>
+                    <includes>
+                        <include>**/*Test.java</include>
+                        <include>**/*TestCase.java</include>
+                    </includes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/trunk/bval-jsr303/src/main/appended-resources/META-INF/NOTICE.vm b/trunk/bval-jsr303/src/main/appended-resources/META-INF/NOTICE.vm
new file mode 100644
index 0000000..05ddae6
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/appended-resources/META-INF/NOTICE.vm
@@ -0,0 +1,26 @@
+##
+## 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.
+##
+
+
+The following copyright notice(s) were affixed to portions of this code
+with which this file is now or was at one time distributed.
+
+This product includes software developed by agimatec GmbH.
+Copyright 2007-2010 Agimatec GmbH. All rights reserved.
+
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/AssertFalseValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/AssertFalseValidator.java
new file mode 100644
index 0000000..6303452
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/AssertFalseValidator.java
@@ -0,0 +1,37 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.AssertFalse;
+
+/**
+ * Description: assert that value is false<br/>
+ */
+public class AssertFalseValidator implements ConstraintValidator<AssertFalse, Boolean> {
+
+    public void initialize(AssertFalse annotation) {
+    }
+
+    public boolean isValid(Boolean value, ConstraintValidatorContext context) {
+        return value == null || !value;
+    }
+
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/AssertTrueValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/AssertTrueValidator.java
new file mode 100644
index 0000000..6b7be14
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/AssertTrueValidator.java
@@ -0,0 +1,37 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.AssertTrue;
+
+/**
+ * Description: assert that value is true<br/>
+ */
+public class AssertTrueValidator implements ConstraintValidator<AssertTrue, Boolean> {
+
+    public void initialize(AssertTrue annotation) {
+    }
+
+    public boolean isValid(Boolean value, ConstraintValidatorContext context) {
+        return value == null || value;
+    }
+
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForNumber.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForNumber.java
new file mode 100644
index 0000000..b9222bd
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForNumber.java
@@ -0,0 +1,53 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.DecimalMax;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/** Description: validate that number-value of passed object is <= maxvalue<br/> */
+public class DecimalMaxValidatorForNumber
+      implements ConstraintValidator<DecimalMax, Number> {
+
+    private BigDecimal maxValue;
+
+    public void initialize(DecimalMax annotation) {
+        try {
+            this.maxValue = new BigDecimal(annotation.value());
+        } catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException(
+                  annotation.value() + " does not represent a valid BigDecimal format");
+        }
+    }
+
+    public boolean isValid(Number value, ConstraintValidatorContext context) {
+        if (value == null) {
+            return true;
+        } else if (value instanceof BigDecimal) {
+            return ((BigDecimal) value).compareTo(maxValue) != 1;
+        } else if (value instanceof BigInteger) {
+            return (new BigDecimal((BigInteger) value)).compareTo(maxValue) != 1;
+        } else {
+            return (new BigDecimal(value.doubleValue()).compareTo(maxValue)) != 1;
+        }
+    }           
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForString.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForString.java
new file mode 100644
index 0000000..5f1a863
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMaxValidatorForString.java
@@ -0,0 +1,54 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.DecimalMax;
+import java.math.BigDecimal;
+
+/**
+ * Check that the String being validated represents a number, and has a value
+ * <= maxvalue
+ */
+public class DecimalMaxValidatorForString
+      implements ConstraintValidator<DecimalMax, String> {
+
+    private BigDecimal maxValue;
+
+    public void initialize(DecimalMax annotation) {
+        try {
+            this.maxValue = new BigDecimal(annotation.value());
+        } catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException(
+                  annotation.value() + " does not represent a valid BigDecimal format");
+        }
+    }
+
+    public boolean isValid(String value, ConstraintValidatorContext context) {
+        if (value == null) {
+            return true;
+        }
+        try {
+            return new BigDecimal(value).compareTo(maxValue) != 1;
+        } catch (NumberFormatException nfe) {
+            return false;
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForNumber.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForNumber.java
new file mode 100644
index 0000000..b24a80f
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForNumber.java
@@ -0,0 +1,53 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.DecimalMin;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/** Description: validate that number-value of passed object is >= minvalue<br/> */
+public class DecimalMinValidatorForNumber
+      implements ConstraintValidator<DecimalMin, Number> {
+
+    private BigDecimal minValue;
+
+    public void initialize(DecimalMin annotation) {
+        try {
+            this.minValue = new BigDecimal(annotation.value());
+        } catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException(
+                  annotation.value() + " does not represent a valid BigDecimal format");
+        }
+    }
+
+    public boolean isValid(Number value, ConstraintValidatorContext context) {
+        if (value == null) {
+            return true;
+        } else if (value instanceof BigDecimal) {
+            return ((BigDecimal) value).compareTo(minValue) != -1;
+        } else if (value instanceof BigInteger) {
+            return (new BigDecimal((BigInteger) value)).compareTo(minValue) != -1;
+        } else {
+            return (new BigDecimal(value.doubleValue()).compareTo(minValue)) != -1;
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForString.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForString.java
new file mode 100644
index 0000000..7859d53
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DecimalMinValidatorForString.java
@@ -0,0 +1,56 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.DecimalMin;
+import java.math.BigDecimal;
+
+/**
+ * Description:
+ * Check that the String being validated represents a number, and has a value
+ * >= minvalue
+ */
+public class DecimalMinValidatorForString
+      implements ConstraintValidator<DecimalMin, String> {
+
+    private BigDecimal minValue;
+
+    public void initialize(DecimalMin annotation) {
+        try {
+            this.minValue = new BigDecimal(annotation.value());
+        } catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException(
+                  annotation.value() + " does not represent a valid BigDecimal format");
+        }
+    }
+
+    public boolean isValid(String value, ConstraintValidatorContext context) {
+        //null values are valid
+        if (value == null) {
+            return true;
+        }
+        try {
+            return new BigDecimal(value).compareTo(minValue) != -1;
+        } catch (NumberFormatException nfe) {
+            return false;
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DigitsValidatorForNumber.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DigitsValidatorForNumber.java
new file mode 100644
index 0000000..5975f22
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DigitsValidatorForNumber.java
@@ -0,0 +1,85 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Digits;
+import java.math.BigDecimal;
+
+/**
+ * Validates that the <code>Number</code> being validates matches the pattern
+ * defined in the constraint.
+ */
+public class DigitsValidatorForNumber implements ConstraintValidator<Digits, Number> {
+
+    private int integral;
+    private int fractional;
+
+    public int getIntegral() {
+        return integral;
+    }
+
+    public void setIntegral(int integral) {
+        this.integral = integral;
+    }
+
+    public int getFractional() {
+        return fractional;
+    }
+
+    public void setFractional(int fractional) {
+        this.fractional = fractional;
+    }
+
+    public void initialize(Digits annotation) {
+        this.integral = annotation.integer();
+        this.fractional = annotation.fraction();
+        if (integral < 0) {
+            throw new IllegalArgumentException(
+                  "The length of the integer part cannot be negative.");
+        }
+        if (fractional < 0) {
+            throw new IllegalArgumentException(
+                  "The length of the fraction part cannot be negative.");
+        }
+    }
+
+    public boolean isValid(Number num, ConstraintValidatorContext context) {
+        if (num == null) {
+            return true;
+        }
+
+        BigDecimal bigDecimal;
+        if (num instanceof BigDecimal) {
+            bigDecimal = (BigDecimal) num;
+        } else {
+            bigDecimal = new BigDecimal(num.toString());
+        }
+        bigDecimal = bigDecimal.stripTrailingZeros();
+
+        int intLength = bigDecimal.precision() - bigDecimal.scale();
+        if (integral >= intLength) {
+            int factionLength = bigDecimal.scale() < 0 ? 0 : bigDecimal.scale();
+            return fractional >= factionLength;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DigitsValidatorForString.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DigitsValidatorForString.java
new file mode 100644
index 0000000..752f887
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/DigitsValidatorForString.java
@@ -0,0 +1,93 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Digits;
+import java.math.BigDecimal;
+
+/**
+ * Validates that the <code>String</code> being validated consists of digits,
+ * and matches the pattern defined in the constraint.
+ */
+public class DigitsValidatorForString implements ConstraintValidator<Digits, String> {
+
+    private int integral;
+    private int fractional;
+
+    public int getIntegral() {
+        return integral;
+    }
+
+    public void setIntegral(int integral) {
+        this.integral = integral;
+    }
+
+    public int getFractional() {
+        return fractional;
+    }
+
+    public void setFractional(int fractional) {
+        this.fractional = fractional;
+    }
+
+    public void initialize(Digits annotation) {
+        this.integral = annotation.integer();
+        this.fractional = annotation.fraction();
+        if (integral < 0) {
+            throw new IllegalArgumentException(
+                  "The length of the integer part cannot be negative.");
+        }
+        if (fractional < 0) {
+            throw new IllegalArgumentException(
+                  "The length of the fraction part cannot be negative.");
+        }
+    }
+
+    public boolean isValid(String str, ConstraintValidatorContext context) {
+        //null values are valid
+        if (str == null) {
+            return true;
+        }
+
+        BigDecimal bigDecimal = getBigDecimalValue(str);
+        if (bigDecimal == null) {
+            return false;
+        }
+
+        int intLength = bigDecimal.precision() - bigDecimal.scale();
+        if (integral >= intLength) {
+            int factionLength = bigDecimal.scale() < 0 ? 0 : bigDecimal.scale();
+            return fractional >= factionLength;
+        } else {
+            return false;
+        }
+    }
+
+    private BigDecimal getBigDecimalValue(String str) {
+        BigDecimal bd;
+        try {
+            bd = new BigDecimal(str);
+        } catch (NumberFormatException nfe) {
+            return null;
+        }
+        return bd;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/Email.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/Email.java
new file mode 100644
index 0000000..dc97236
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/Email.java
@@ -0,0 +1,49 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * <p>
+ * --
+ * TODO - This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * --
+ * </p>
+ * Description: annotation to validate an email address (by pattern)<br/>
+ */
+@Documented
+@Constraint(validatedBy = EmailValidator.class)
+@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER})
+@Retention(RUNTIME)
+public @interface Email {
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.constraints.Email.message}";
+
+    Class<? extends Payload>[] payload() default {};
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/EmailValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/EmailValidator.java
new file mode 100644
index 0000000..39dd845
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/EmailValidator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.bval.constraints;
+
+import org.apache.bval.routines.EMailValidationUtils;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+
+/**
+ * Description: <br/>
+ */
+public class EmailValidator implements ConstraintValidator<Email, CharSequence> {
+
+    public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
+        return EMailValidationUtils.isValid(value);
+    }
+
+    public void initialize(Email parameters) {
+        // do nothing (as long as Email has no properties)
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/FutureValidatorForCalendar.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/FutureValidatorForCalendar.java
new file mode 100644
index 0000000..920ecd8
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/FutureValidatorForCalendar.java
@@ -0,0 +1,47 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Future;
+import java.util.Calendar;
+
+/**
+ * Description: validate a date or calendar representing a date in the future <br/>
+ */
+public class FutureValidatorForCalendar implements ConstraintValidator<Future, Calendar> {
+
+    public void initialize(Future annotation) {
+    }
+
+    public boolean isValid(Calendar cal, ConstraintValidatorContext context) {
+        return cal == null || cal.after(now());
+    }
+
+
+    /**
+     * overwrite when you need a different algorithm for 'now'.
+     *
+     * @return current date/time
+     */
+    protected Calendar now() {
+        return Calendar.getInstance();
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/FutureValidatorForDate.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/FutureValidatorForDate.java
new file mode 100644
index 0000000..7dcd169
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/FutureValidatorForDate.java
@@ -0,0 +1,46 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Future;
+import java.util.Date;
+
+/**
+ * Description: validate a date or calendar representing a date in the future <br/>
+ */
+public class FutureValidatorForDate implements ConstraintValidator<Future, Date> {
+
+    public void initialize(Future annotation) {
+    }
+
+    public boolean isValid(Date date, ConstraintValidatorContext context) {
+        return date == null || date.after(now());
+    }
+
+    /**
+     * overwrite when you need a different algorithm for 'now'.
+     *
+     * @return current date/time
+     */
+    protected Date now() {
+        return new Date();
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MaxValidatorForNumber.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MaxValidatorForNumber.java
new file mode 100644
index 0000000..deaf9ea
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MaxValidatorForNumber.java
@@ -0,0 +1,50 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Max;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Check that the number being validated is less than or equal to the maximum
+ * value specified.
+ */
+public class MaxValidatorForNumber implements ConstraintValidator<Max, Number> {
+
+    private long max;
+
+    public void initialize(Max annotation) {
+        this.max = annotation.value();
+    }
+
+    public boolean isValid(Number value, ConstraintValidatorContext context) {
+        if (value == null) {
+            return true;
+        } else if (value instanceof BigDecimal) {
+            return ((BigDecimal) value).compareTo(BigDecimal.valueOf(max)) != 1;
+        } else if (value instanceof BigInteger) {
+            return ((BigInteger) value).compareTo(BigInteger.valueOf(max)) != 1;
+        } else {
+            return value.longValue() <= max;
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MaxValidatorForString.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MaxValidatorForString.java
new file mode 100644
index 0000000..44a2bcd
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MaxValidatorForString.java
@@ -0,0 +1,48 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Max;
+import java.math.BigDecimal;
+
+/**
+ * Check that the String being validated represents a number, and has a value
+ * less than or equal to the maximum value specified.
+ */
+public class MaxValidatorForString implements ConstraintValidator<Max, String> {
+
+    private long max;
+
+    public void initialize(Max annotation) {
+        this.max = annotation.value();
+    }
+
+    public boolean isValid(String value, ConstraintValidatorContext context) {
+        if (value == null) {
+            return true;
+        }
+        try {
+            return new BigDecimal(value).compareTo(BigDecimal.valueOf(max)) != 1;
+        } catch (NumberFormatException nfe) {
+            return false;
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MinValidatorForNumber.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MinValidatorForNumber.java
new file mode 100644
index 0000000..7c537b9
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MinValidatorForNumber.java
@@ -0,0 +1,50 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Min;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Description: validate that number-value of passed object is >= min-value<br/>
+ */
+public class MinValidatorForNumber implements ConstraintValidator<Min, Number> {
+
+    private long minValue;
+
+    public void initialize(Min annotation) {
+        this.minValue = annotation.value();
+    }
+
+    public boolean isValid(Number value, ConstraintValidatorContext context) {
+        if (value == null) {
+            return true;
+        } else if (value instanceof BigDecimal) {
+            return ((BigDecimal) value).compareTo(BigDecimal.valueOf(minValue)) != -1;
+        } else if (value instanceof BigInteger) {
+            return ((BigInteger) value).compareTo(BigInteger.valueOf(minValue)) != -1;
+        } else {
+            return value.longValue() >= minValue;
+        }
+
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MinValidatorForString.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MinValidatorForString.java
new file mode 100644
index 0000000..be268dd
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/MinValidatorForString.java
@@ -0,0 +1,48 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Min;
+import java.math.BigDecimal;
+
+/**
+ * Check that the String being validated represents a number, and has a value
+ * more than or equal to the minimum value specified.
+ */
+public class MinValidatorForString implements ConstraintValidator<Min, String> {
+
+    private long minValue;
+
+    public void initialize(Min annotation) {
+        this.minValue = annotation.value();
+    }
+
+    public boolean isValid(String value, ConstraintValidatorContext context) {
+        if (value == null) {
+            return true;
+        }
+        try {
+            return new BigDecimal(value).compareTo(BigDecimal.valueOf(minValue)) != -1;
+        } catch (NumberFormatException nfe) {
+            return false;
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmpty.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmpty.java
new file mode 100644
index 0000000..a517873
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmpty.java
@@ -0,0 +1,48 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * <pre>
+ * This class is NOT part of the bean_validation spec and might disappear
+ * as soon as a final version of the specification contains a similar functionality.
+ * </pre>
+ */
+@Documented
+@Constraint(
+      validatedBy = {NotEmptyValidatorForCollection.class, NotEmptyValidatorForMap.class,
+            NotEmptyValidatorForString.class, NotEmptyValidator.class})
+@Target({METHOD, FIELD, ANNOTATION_TYPE, PARAMETER})
+@Retention(RUNTIME)
+public @interface NotEmpty {
+    Class<?>[] groups() default {};
+
+    String message() default "{org.apache.bval.constraints.NotEmpty.message}";
+
+    Class<? extends Payload>[] payload() default {};
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidator.java
new file mode 100644
index 0000000..2cb2491
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidator.java
@@ -0,0 +1,56 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Description:  Check the non emptyness of an
+ * any object that has a public isEmpty():boolean or a valid toString() method
+ */
+public class NotEmptyValidator implements ConstraintValidator<NotEmpty, Object> {
+    public void initialize(NotEmpty constraintAnnotation) {
+        // do nothing
+    }
+
+    public boolean isValid(Object value, ConstraintValidatorContext context) {
+        if (value == null) return true;
+        if (value.getClass().isArray()) {
+            return Array.getLength(value) > 0;
+        } else {
+            try {
+                Method isEmptyMethod = value.getClass().getMethod("isEmpty");
+                if (isEmptyMethod != null) {
+                    return !((Boolean) isEmptyMethod.invoke(value)).booleanValue();
+                }
+            } catch (IllegalAccessException iae) {
+                // do nothing
+            } catch (NoSuchMethodException nsme) {
+                // do nothing
+            } catch (InvocationTargetException ite) {
+                // do nothing
+            }
+            return value.toString().length() > 0;
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCollection.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCollection.java
new file mode 100644
index 0000000..8cc0f3a
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForCollection.java
@@ -0,0 +1,36 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.util.Collection;
+
+/**
+ * Description: <br/>
+ */
+public class NotEmptyValidatorForCollection implements ConstraintValidator<NotEmpty, Collection<?>> {
+    public void initialize(NotEmpty constraintAnnotation) {
+        // do nothing
+    }
+
+    public boolean isValid(Collection<?> value, ConstraintValidatorContext context) {
+        return value == null || !value.isEmpty();
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForMap.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForMap.java
new file mode 100644
index 0000000..cca28bd
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForMap.java
@@ -0,0 +1,36 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.util.Map;
+
+/**
+ * Description: <br/>
+ */
+public class NotEmptyValidatorForMap implements ConstraintValidator<NotEmpty, Map<?, ?>> {
+    public void initialize(NotEmpty constraintAnnotation) {
+        // do nothing
+    }
+
+    public boolean isValid(Map<?, ?> value, ConstraintValidatorContext context) {
+        return value == null || !value.isEmpty();
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForString.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForString.java
new file mode 100644
index 0000000..2775c8e
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotEmptyValidatorForString.java
@@ -0,0 +1,35 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Description: <br/>
+ */
+public class NotEmptyValidatorForString implements ConstraintValidator<NotEmpty, String> {
+    public void initialize(NotEmpty constraintAnnotation) {
+        // do nothing
+    }
+
+    public boolean isValid(String value, ConstraintValidatorContext context) {
+        return value == null || value.length() > 0;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotNullValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotNullValidator.java
new file mode 100644
index 0000000..913dc22
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NotNullValidator.java
@@ -0,0 +1,34 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.NotNull;
+
+/** valid when object is NOT null */
+public class NotNullValidator implements ConstraintValidator<NotNull, Object> {
+    public void initialize(NotNull constraintAnnotation) {
+        // do nothing
+    }
+
+    public boolean isValid(Object value, ConstraintValidatorContext context) {
+        return value != null;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NullValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NullValidator.java
new file mode 100644
index 0000000..0dfa85f
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/NullValidator.java
@@ -0,0 +1,37 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Null;
+
+/**
+ * Description: valid when object is null<br/>
+ */
+public class NullValidator implements ConstraintValidator<Null, Object> {
+
+    public void initialize(Null annotation) {
+        // do nothing
+    }
+
+    public boolean isValid(Object object, ConstraintValidatorContext context) {
+        return object == null;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PastValidatorForCalendar.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PastValidatorForCalendar.java
new file mode 100644
index 0000000..fee0dc3
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PastValidatorForCalendar.java
@@ -0,0 +1,47 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Past;
+import java.util.Calendar;
+
+/**
+ * Description: validate a date or calendar representing a date in the past<br/>
+ */
+public class PastValidatorForCalendar implements ConstraintValidator<Past, Calendar> {
+
+    public void initialize(Past annotation) {
+    }
+
+    public boolean isValid(Calendar cal, ConstraintValidatorContext context) {
+        return cal == null || cal.before(now());
+    }
+
+
+    /**
+     * overwrite when you need a different algorithm for 'now'.
+     *
+     * @return current date/time
+     */
+    protected Calendar now() {
+        return Calendar.getInstance();
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PastValidatorForDate.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PastValidatorForDate.java
new file mode 100644
index 0000000..78d88d1
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PastValidatorForDate.java
@@ -0,0 +1,46 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Past;
+import java.util.Date;
+
+/**
+ * Description: validate a date or calendar representing a date in the past<br/>
+ */
+public class PastValidatorForDate implements ConstraintValidator<Past, Date> {
+
+    public void initialize(Past annotation) {
+    }
+
+    public boolean isValid(Date date, ConstraintValidatorContext context) {
+        return date == null || date.before(now());
+    }
+
+    /**
+     * overwrite when you need a different algorithm for 'now'.
+     *
+     * @return current date/time
+     */
+    protected Date now() {
+        return new Date();
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PatternValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PatternValidator.java
new file mode 100644
index 0000000..8d73561
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/PatternValidator.java
@@ -0,0 +1,51 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * validator using a regular expression,
+ * based on the jsr303 Pattern constraint annotation.
+ */
+public class PatternValidator implements ConstraintValidator<Pattern, String> {
+    protected java.util.regex.Pattern pattern;
+
+    public void initialize(Pattern annotation) {
+        Pattern.Flag flags[] = annotation.flags();
+        int intFlag = 0;
+        for (Pattern.Flag flag : flags) {
+            intFlag = intFlag | flag.getValue();
+        }
+
+        try {
+            pattern = java.util.regex.Pattern.compile(annotation.regexp(), intFlag);
+        } catch (PatternSyntaxException e) {
+            throw new IllegalArgumentException("Invalid regular expression.", e);
+        }
+    }
+
+
+    public boolean isValid(String value, ConstraintValidatorContext context) {
+        return value == null || pattern.matcher(value).matches();
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidator.java
new file mode 100644
index 0000000..7dc68fd
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidator.java
@@ -0,0 +1,44 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ValidationException;
+import javax.validation.constraints.Size;
+
+/**
+ * Description: Abstract validator impl. for @Size annotation<br/>
+ */
+public abstract class SizeValidator {
+    protected int min;
+    protected int max;
+
+    /**
+     * Configure the constraint validator based on the elements
+     * specified at the time it was defined.
+     *
+     * @param constraint the constraint definition
+     */
+    public void initialize(Size constraint) {
+        min = constraint.min();
+        max = constraint.max();
+        if (min < 0) throw new ValidationException("Min cannot be negative");
+        if (max < 0) throw new ValidationException("Max cannot be negative");
+        if (max < min) throw new ValidationException("Max cannot be less than Min");
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfBoolean.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfBoolean.java
new file mode 100644
index 0000000..b4abf4a
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfBoolean.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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfBoolean extends SizeValidator
+      implements ConstraintValidator<Size, boolean[]> {
+
+    /**
+     * Checks the number of entries in an array.
+     *
+     * @param array   The array to validate.
+     * @param context context in which the constraint is evaluated.
+     * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+     *         <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+     *         <code>false</code> otherwise.
+     */
+    public boolean isValid(boolean[] array, ConstraintValidatorContext context) {
+        if (array == null) {
+            return true;
+        }
+        int length = Array.getLength(array);
+        return length >= min && length <= max;
+	}
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfByte.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfByte.java
new file mode 100644
index 0000000..8ee17ce
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfByte.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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfByte extends SizeValidator
+      implements ConstraintValidator<Size, byte[]> {
+
+    /**
+     * Checks the number of entries in an array.
+     *
+     * @param array   The array to validate.
+     * @param context context in which the constraint is evaluated.
+     * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+     *         <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+     *         <code>false</code> otherwise.
+     */
+    public boolean isValid(byte[] array, ConstraintValidatorContext context) {
+        if (array == null) {
+            return true;
+        }
+        int length = Array.getLength(array);
+        return length >= min && length <= max;
+	}
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfChar.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfChar.java
new file mode 100644
index 0000000..1466165
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfChar.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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfChar extends SizeValidator
+      implements ConstraintValidator<Size, char[]> {
+
+    /**
+     * Checks the number of entries in an array.
+     *
+     * @param array   The array to validate.
+     * @param context context in which the constraint is evaluated.
+     * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+     *         <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+     *         <code>false</code> otherwise.
+     */
+    public boolean isValid(char[] array, ConstraintValidatorContext context) {
+        if (array == null) {
+            return true;
+        }
+        int length = Array.getLength(array);
+        return length >= min && length <= max;
+	}
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfDouble.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfDouble.java
new file mode 100644
index 0000000..4676e5f
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfDouble.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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfDouble extends SizeValidator
+      implements ConstraintValidator<Size, double[]> {
+
+    /**
+     * Checks the number of entries in an array.
+     *
+     * @param array   The array to validate.
+     * @param context context in which the constraint is evaluated.
+     * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+     *         <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+     *         <code>false</code> otherwise.
+     */
+    public boolean isValid(double[] array, ConstraintValidatorContext context) {
+        if (array == null) {
+            return true;
+        }
+        int length = Array.getLength(array);
+        return length >= min && length <= max;
+	}
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfFloat.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfFloat.java
new file mode 100644
index 0000000..71b3b64
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfFloat.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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfFloat extends SizeValidator
+      implements ConstraintValidator<Size, float[]> {
+
+    /**
+     * Checks the number of entries in an array.
+     *
+     * @param array   The array to validate.
+     * @param context context in which the constraint is evaluated.
+     * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+     *         <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+     *         <code>false</code> otherwise.
+     */
+    public boolean isValid(float[] array, ConstraintValidatorContext context) {
+        if (array == null) {
+            return true;
+        }
+        int length = Array.getLength(array);
+        return length >= min && length <= max;
+	}
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfInt.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfInt.java
new file mode 100644
index 0000000..ba2f6f3
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfInt.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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfInt extends SizeValidator
+      implements ConstraintValidator<Size, int[]> {
+
+    /**
+     * Checks the number of entries in an array.
+     *
+     * @param array   The array to validate.
+     * @param context context in which the constraint is evaluated.
+     * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+     *         <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+     *         <code>false</code> otherwise.
+     */
+    public boolean isValid(int[] array, ConstraintValidatorContext context) {
+        if (array == null) {
+            return true;
+        }
+        int length = Array.getLength(array);
+        return length >= min && length <= max;
+	}
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfLong.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfLong.java
new file mode 100644
index 0000000..2965ceb
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfLong.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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfLong extends SizeValidator
+      implements ConstraintValidator<Size, long[]> {
+
+    /**
+     * Checks the number of entries in an array.
+     *
+     * @param array   The array to validate.
+     * @param context context in which the constraint is evaluated.
+     * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+     *         <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+     *         <code>false</code> otherwise.
+     */
+    public boolean isValid(long[] array, ConstraintValidatorContext context) {
+        if (array == null) {
+            return true;
+        }
+        int length = Array.getLength(array);
+        return length >= min && length <= max;
+	}
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfObject.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfObject.java
new file mode 100644
index 0000000..16a450c
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfObject.java
@@ -0,0 +1,46 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+/** Check that the length of an array is between <i>min</i> and <i>max</i> */
+public class SizeValidatorForArrayOfObject extends SizeValidator
+      implements ConstraintValidator<Size, Object[]> {
+    /**
+     * Checks the number of entries in an array.
+     *
+     * @param array   The array to validate.
+     * @param context context in which the constraint is evaluated.
+     * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+     *         <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+     *         <code>false</code> otherwise.
+     */
+    public boolean isValid(Object[] array, ConstraintValidatorContext context) {
+        if (array == null) {
+            return true;
+        }
+        int length = Array.getLength(array);
+        return length >= min && length <= max;
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfShort.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfShort.java
new file mode 100644
index 0000000..df42faa
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForArrayOfShort.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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Array;
+
+public class SizeValidatorForArrayOfShort extends SizeValidator
+      implements ConstraintValidator<Size, short[]> {
+
+    /**
+     * Checks the number of entries in an array.
+     *
+     * @param array   The array to validate.
+     * @param context context in which the constraint is evaluated.
+     * @return Returns <code>true</code> if the array is <code>null</code> or the number of entries in
+     *         <code>array</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+     *         <code>false</code> otherwise.
+     */
+    public boolean isValid(short[] array, ConstraintValidatorContext context) {
+        if (array == null) {
+            return true;
+        }
+        int length = Array.getLength(array);
+        return length >= min && length <= max;
+	}
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForCollection.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForCollection.java
new file mode 100644
index 0000000..f77a301
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForCollection.java
@@ -0,0 +1,47 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.util.Collection;
+
+/** Check that a Collection's size is between min and max. */
+public class SizeValidatorForCollection extends SizeValidator
+      implements ConstraintValidator<Size, Collection<?>> {
+
+    /**
+     * Checks the number of entries in a map.
+     *
+     * @param collection The collection to validate.
+     * @param context    context in which the constraint is evaluated.
+     * @return Returns <code>true</code> if the collection is <code>null</code> or the number of entries in
+     *         <code>collection</code> is between the specified <code>min</code> and <code>max</code> values (inclusive),
+     *         <code>false</code> otherwise.
+     */
+    public boolean isValid(Collection<?> collection, ConstraintValidatorContext context) {
+        if (collection == null) {
+            return true;
+        }
+        int length = collection.size();
+        return length >= min && length <= max;
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForMap.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForMap.java
new file mode 100644
index 0000000..7d73cc3
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForMap.java
@@ -0,0 +1,46 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+import java.util.Map;
+
+/** Check that a Map's size is between min and max. */
+public class SizeValidatorForMap extends SizeValidator
+      implements ConstraintValidator<Size, Map<?, ?>> {
+    /**
+     * Checks the number of entries in a map.
+     *
+     * @param map     The map to validate.
+     * @param context context in which the constraint is evaluated.
+     * @return Returns <code>true</code> if the map is <code>null</code> or the number of entries in <code>map</code>
+     *         is between the specified <code>min</code> and <code>max</code> values (inclusive),
+     *         <code>false</code> otherwise.
+     */
+    public boolean isValid(Map<?, ?> map, ConstraintValidatorContext context) {
+        if (map == null) {
+            return true;
+        }
+        int size = map.size();
+        return size >= min && size <= max;
+	}
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForString.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForString.java
new file mode 100644
index 0000000..53324c9
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/constraints/SizeValidatorForString.java
@@ -0,0 +1,44 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.constraints.Size;
+
+/** Check that a string's length is between min and max. */
+public class SizeValidatorForString extends SizeValidator
+      implements ConstraintValidator<Size, String> {
+    /**
+     * Checks the length of the specified string.
+     *
+     * @param s       The string to validate.
+     * @param context context in which the constraint is evaluated.
+     * @return Returns <code>true</code> if the string is <code>null</code> or the length of <code>s</code> between the specified
+     *         <code>min</code> and <code>max</code> values (inclusive), <code>false</code> otherwise.
+     */
+    public boolean isValid(String s, ConstraintValidatorContext context) {
+        if (s == null) {
+            return true;
+        }
+        int length = s.length();
+        return length >= min && length <= max;
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationConstraintBuilder.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationConstraintBuilder.java
new file mode 100644
index 0000000..d6bf24d
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationConstraintBuilder.java
@@ -0,0 +1,276 @@
+/*
+ * 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.bval.jsr303;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.validation.ConstraintDeclarationException;
+import javax.validation.ConstraintValidator;
+import javax.validation.OverridesAttribute;
+import javax.validation.Payload;
+import javax.validation.ReportAsSingleViolation;
+
+import org.apache.bval.jsr303.groups.GroupsComputer;
+import org.apache.bval.jsr303.xml.AnnotationProxyBuilder;
+import org.apache.bval.util.AccessStrategy;
+
+/**
+ * Description: helper class that builds a {@link ConstraintValidation} or its
+ * composite constraint validations by parsing the jsr303-annotations and
+ * providing information (e.g. for @OverridesAttributes) <br/>
+ */
+final class AnnotationConstraintBuilder<A extends Annotation> {
+    private static final Logger log = Logger.getLogger(AnnotationConstraintBuilder.class.getName());
+
+    private final ConstraintValidation<?> constraintValidation;
+    private List<ConstraintOverrides> overrides;
+
+    /**
+     * Create a new AnnotationConstraintBuilder instance.
+     * 
+     * @param validatorClasses
+     * @param constraintValidator
+     * @param annotation
+     * @param owner
+     * @param access
+     */
+    public AnnotationConstraintBuilder(Class<? extends ConstraintValidator<A, ?>>[] validatorClasses,
+        ConstraintValidator<A, ?> constraintValidator, A annotation, Class<?> owner, AccessStrategy access) {
+        boolean reportFromComposite =
+            annotation != null && annotation.annotationType().isAnnotationPresent(ReportAsSingleViolation.class);
+        constraintValidation =
+            new ConstraintValidation<A>(validatorClasses, constraintValidator, annotation, owner, access,
+                reportFromComposite);
+        buildFromAnnotation();
+    }
+
+    /** build attributes, payload, groups from 'annotation' */
+    private void buildFromAnnotation() {
+        if (constraintValidation.getAnnotation() != null) {
+            run(new PrivilegedAction<Object>() {
+                public Object run() {
+                    for (Method method : constraintValidation.getAnnotation().annotationType().getDeclaredMethods()) {
+                        // groups + payload must also appear in attributes (also
+                        // checked by TCK-Tests)
+                        if (method.getParameterTypes().length == 0) {
+                            try {
+                                if (ConstraintAnnotationAttributes.PAYLOAD.getAttributeName().equals(method.getName())) {
+                                    buildPayload(method);
+                                } else if (ConstraintAnnotationAttributes.GROUPS.getAttributeName().equals(
+                                    method.getName())) {
+                                    buildGroups(method);
+                                } else {
+                                    constraintValidation.getAttributes().put(method.getName(),
+                                        method.invoke(constraintValidation.getAnnotation()));
+                                }
+                            } catch (Exception e) { // do nothing
+                                log.log(Level.WARNING, String.format("Error processing annotation: %s ", constraintValidation.getAnnotation()), e);
+                            }
+                        }
+                    }
+                    return null;
+                }
+            });
+        }
+    }
+
+    private void buildGroups(Method method) throws IllegalAccessException, InvocationTargetException {
+        Object raw = method.invoke(constraintValidation.getAnnotation());
+        Class<?>[] garr;
+        if (raw instanceof Class<?>) {
+            garr = new Class[] { (Class<?>) raw };
+        } else if (raw instanceof Class<?>[]) {
+            garr = (Class<?>[]) raw;
+        } else {
+            garr = null;
+        }
+
+        if (garr == null || garr.length == 0) {
+            garr = GroupsComputer.getDefaultGroupArray();
+        }
+        constraintValidation.setGroups(new HashSet<Class<?>>(Arrays.asList(garr)));
+    }
+
+    @SuppressWarnings("unchecked")
+    private void buildPayload(Method method) throws IllegalAccessException, InvocationTargetException {
+        Class<? extends Payload>[] payload_raw =
+            (Class<? extends Payload>[]) method.invoke(constraintValidation.getAnnotation());
+        Set<Class<? extends Payload>> payloadSet;
+        if (payload_raw == null) {
+            payloadSet = Collections.<Class<? extends Payload>> emptySet();
+        } else {
+            payloadSet = new HashSet<Class<? extends Payload>>(payload_raw.length);
+            payloadSet.addAll(Arrays.asList(payload_raw));
+        }
+        constraintValidation.setPayload(payloadSet);
+    }
+
+    /**
+     * Get the configured {@link ConstraintValidation}.
+     * 
+     * @return {@link ConstraintValidation}
+     */
+    public ConstraintValidation<?> getConstraintValidation() {
+        return constraintValidation;
+    }
+
+    /**
+     * initialize a child composite 'validation' with @OverridesAttribute from
+     * 'constraintValidation' and add to composites.
+     */
+    public void addComposed(ConstraintValidation<?> composite) {
+        applyOverridesAttributes(composite);
+        constraintValidation.addComposed(composite); // add AFTER apply()
+    }
+
+    private void applyOverridesAttributes(ConstraintValidation<?> composite) {
+        if (null == overrides) {
+            buildOverridesAttributes();
+        }
+        if (!overrides.isEmpty()) {
+            int index = computeIndex(composite);
+
+            // Search for the overrides to apply
+            ConstraintOverrides generalOverride = findOverride(composite.getAnnotation().annotationType(), -1);
+            if (generalOverride != null) {
+                if (index > 0) {
+                    throw new ConstraintDeclarationException("Wrong OverridesAttribute declaration for "
+                        + generalOverride.constraintType
+                        + ", it needs a defined index when there is a list of constraints");
+                }
+                generalOverride.applyOn(composite);
+            }
+
+            ConstraintOverrides override = findOverride(composite.getAnnotation().annotationType(), index);
+            if (override != null) {
+                override.applyOn(composite);
+            }
+
+        }
+    }
+
+    /**
+     * Calculates the index of the composite constraint. The index represents
+     * the order in which it is added in reference to other constraints of the
+     * same type.
+     * 
+     * @param composite
+     *            The composite constraint (not yet added).
+     * @return An integer index always >= 0
+     */
+    private int computeIndex(ConstraintValidation<?> composite) {
+        int idx = 0;
+        for (ConstraintValidation<?> each : constraintValidation.getComposingValidations()) {
+            if (each.getAnnotation().annotationType() == composite.getAnnotation().annotationType()) {
+                idx++;
+            }
+        }
+        return idx;
+    }
+
+    /** read overridesAttributes from constraintValidation.annotation */
+    private void buildOverridesAttributes() {
+        overrides = new LinkedList<ConstraintOverrides>();
+        for (Method method : constraintValidation.getAnnotation().annotationType().getDeclaredMethods()) {
+            OverridesAttribute.List annoOAL = method.getAnnotation(OverridesAttribute.List.class);
+            if (annoOAL != null) {
+                for (OverridesAttribute annoOA : annoOAL.value()) {
+                    parseConstraintOverride(method.getName(), annoOA);
+                }
+            }
+            OverridesAttribute annoOA = method.getAnnotation(OverridesAttribute.class);
+            if (annoOA != null) {
+                parseConstraintOverride(method.getName(), annoOA);
+            }
+        }
+    }
+
+    private void parseConstraintOverride(String methodName, OverridesAttribute oa) {
+        ConstraintOverrides target = findOverride(oa.constraint(), oa.constraintIndex());
+        if (target == null) {
+            target = new ConstraintOverrides(oa.constraint(), oa.constraintIndex());
+            overrides.add(target);
+        }
+        target.values.put(oa.name(), constraintValidation.getAttributes().get(methodName));
+    }
+
+    private ConstraintOverrides findOverride(Class<? extends Annotation> constraint, int constraintIndex) {
+        for (ConstraintOverrides each : overrides) {
+            if (each.constraintType == constraint && each.constraintIndex == constraintIndex) {
+                return each;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Holds the values to override in a composed constraint during creation of
+     * a composed ConstraintValidation
+     */
+    private static final class ConstraintOverrides {
+        final Class<? extends Annotation> constraintType;
+        final int constraintIndex;
+
+        /** key = attributeName, value = overridden value */
+        final Map<String, Object> values;
+
+        private ConstraintOverrides(Class<? extends Annotation> constraintType, int constraintIndex) {
+            this.constraintType = constraintType;
+            this.constraintIndex = constraintIndex;
+            values = new HashMap<String, Object>();
+        }
+
+        @SuppressWarnings("unchecked")
+        private void applyOn(ConstraintValidation<?> composite) {
+            // Update the attributes
+            composite.getAttributes().putAll(values);
+
+            // And the annotation
+            Annotation originalAnnot = composite.getAnnotation();
+            AnnotationProxyBuilder<Annotation> apb = new AnnotationProxyBuilder<Annotation>(originalAnnot);
+            for (String key : values.keySet()) {
+                apb.putValue(key, values.get(key));
+            }
+            Annotation newAnnot = apb.createAnnotation();
+            ((ConstraintValidation<Annotation>) composite).setAnnotation(newAnnot);
+        }
+    }
+
+    private static <T> T run(PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationProcessor.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationProcessor.java
new file mode 100644
index 0000000..5629e80
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AnnotationProcessor.java
@@ -0,0 +1,450 @@
+/*
+ * 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.bval.jsr303;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintValidator;
+import javax.validation.UnexpectedTypeException;
+import javax.validation.Valid;
+import javax.validation.ValidationException;
+import javax.validation.groups.Default;
+
+import org.apache.bval.jsr303.util.ConstraintDefinitionValidator;
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+/**
+ * Description: implements uniform handling of JSR303 {@link Constraint}
+ * annotations, including composed constraints and the resolution of
+ * {@link ConstraintValidator} implementations.
+ * 
+ * @version $Rev: 996992 $ $Date: 2010-09-14 12:05:40 -0500 (Tue, 14 Sep 2010) $
+ */
+public final class AnnotationProcessor {
+    /** {@link ApacheFactoryContext} used */
+    private final ApacheFactoryContext factoryContext;
+
+    /**
+     * Create a new {@link AnnotationProcessor} instance.
+     * 
+     * @param factoryContext
+     */
+    public AnnotationProcessor(ApacheFactoryContext factoryContext) {
+        this.factoryContext = factoryContext;
+    }
+
+    /**
+     * Process JSR303 annotations.
+     * 
+     * @param prop
+     *            potentially null
+     * @param owner
+     *            bean type
+     * @param element
+     *            whose annotations to read
+     * @param access
+     *            strategy for <code>prop</code>
+     * @param appender
+     *            handling accumulation
+     * @return whether any processing took place
+     * @throws IllegalAccessException
+     * @throws InvocationTargetException
+     */
+    public boolean processAnnotations(MetaProperty prop, Class<?> owner, AnnotatedElement element,
+        AccessStrategy access, AppendValidation appender) throws IllegalAccessException, InvocationTargetException {
+
+        boolean changed = false;
+        for (Annotation annotation : element.getDeclaredAnnotations()) {
+            changed |= processAnnotation(annotation, prop, owner, access, appender);
+        }
+        return changed;
+    }
+
+    /**
+     * Convenience method to process a single class-level annotation.
+     * 
+     * @param <A>
+     *            annotation type
+     * @param annotation
+     *            to process
+     * @param owner
+     *            bean type
+     * @param appender
+     *            handling accumulation
+     * @return whether any processing took place
+     * @throws IllegalAccessException
+     * @throws InvocationTargetException
+     */
+    public final <A extends Annotation> boolean processAnnotation(A annotation, Class<?> owner, AppendValidation appender)
+        throws IllegalAccessException, InvocationTargetException {
+        return processAnnotation(annotation, null, owner, null, appender);
+    }
+
+    /**
+     * Process a single annotation.
+     * 
+     * @param <A>
+     *            annotation type
+     * @param annotation
+     *            to process
+     * @param prop
+     *            potentially null
+     * @param owner
+     *            bean type
+     * @param access
+     *            strategy for <code>prop</code>
+     * @param appender
+     *            handling accumulation
+     * @return whether any processing took place
+     * @throws IllegalAccessException
+     * @throws InvocationTargetException
+     */
+    public <A extends Annotation> boolean processAnnotation(A annotation, MetaProperty prop, Class<?> owner,
+        AccessStrategy access, AppendValidation appender) throws IllegalAccessException, InvocationTargetException {
+        if (annotation instanceof Valid) {
+            return addAccessStrategy(prop, access);
+        }
+        /**
+         * An annotation is considered a constraint definition if its retention
+         * policy contains RUNTIME and if the annotation itself is annotated
+         * with javax.validation.Constraint.
+         */
+        Constraint vcAnno = annotation.annotationType().getAnnotation(Constraint.class);
+        if (vcAnno != null) {
+            ConstraintDefinitionValidator.validateConstraintDefinition(annotation);
+            Class<? extends ConstraintValidator<A, ?>>[] validatorClasses;
+            validatorClasses = findConstraintValidatorClasses(annotation, vcAnno);
+            return applyConstraint(annotation, validatorClasses, prop, owner, access, appender);
+        }
+        /**
+         * Multi-valued constraints: To support this requirement, the bean
+         * validation provider treats regular annotations (annotations not
+         * annotated by @Constraint) whose value element has a return type of an
+         * array of constraint annotations in a special way.
+         */
+        Object result =
+            SecureActions.getAnnotationValue(annotation, ConstraintAnnotationAttributes.VALUE.getAttributeName());
+        if (result instanceof Annotation[]) {
+            boolean changed = false;
+            for (Annotation each : (Annotation[]) result) {
+                changed |= processAnnotation(each, prop, owner, access, appender);
+            }
+            return changed;
+        }
+        return false;
+    }
+
+    /**
+     * Add the specified {@link AccessStrategy} to <code>prop</code>; noop if
+     * <code>prop == null</code>.
+     * 
+     * @param prop
+     * @param access
+     * @return whether anything took place.
+     */
+    public boolean addAccessStrategy(MetaProperty prop, AccessStrategy access) {
+        if (prop == null) {
+            return false;
+        }
+        AccessStrategy[] strategies = prop.getFeature(Features.Property.REF_CASCADE);
+        if (strategies == null) {
+            strategies = new AccessStrategy[] { access };
+            prop.putFeature(Features.Property.REF_CASCADE, strategies);
+        } else if (!ArrayUtils.contains(strategies, access)) {
+            prop.putFeature(Features.Property.REF_CASCADE, ArrayUtils.add(strategies, access));
+        }
+        return true;
+    }
+
+    /**
+     * Find available {@link ConstraintValidation} classes for a given
+     * constraint annotation.
+     * 
+     * @param annotation
+     * @param vcAnno
+     * @return {@link ConstraintValidation} implementation class array
+     */
+    @SuppressWarnings("unchecked")
+    private <A extends Annotation> Class<? extends ConstraintValidator<A, ?>>[] findConstraintValidatorClasses(
+        A annotation, Constraint vcAnno) {
+        if (vcAnno == null) {
+            vcAnno = annotation.annotationType().getAnnotation(Constraint.class);
+        }
+        Class<? extends ConstraintValidator<A, ?>>[] validatorClasses;
+        Class<A> annotationType = (Class<A>) annotation.annotationType();
+        validatorClasses = factoryContext.getFactory().getConstraintsCache().getConstraintValidators(annotationType);
+        if (validatorClasses == null) {
+            validatorClasses = (Class<? extends ConstraintValidator<A, ?>>[]) vcAnno.validatedBy();
+            if (validatorClasses.length == 0) {
+                validatorClasses =
+                    factoryContext.getFactory().getDefaultConstraints().getValidatorClasses(annotationType);
+            }
+        }
+        return validatorClasses;
+    }
+
+    /**
+     * Apply a constraint to the specified <code>appender</code>.
+     * 
+     * @param annotation
+     *            constraint annotation
+     * @param constraintClasses
+     *            known {@link ConstraintValidator} implementation classes for
+     *            <code>annotation</code>
+     * @param prop
+     *            meta-property
+     * @param owner
+     *            type
+     * @param access
+     *            strategy
+     * @param appender
+     * @return success flag
+     * @throws IllegalAccessException
+     * @throws InvocationTargetException
+     */
+    private <A extends Annotation> boolean applyConstraint(A annotation,
+        Class<? extends ConstraintValidator<A, ?>>[] constraintClasses, MetaProperty prop, Class<?> owner,
+        AccessStrategy access, AppendValidation appender) throws IllegalAccessException, InvocationTargetException {
+
+        final ConstraintValidator<A, ?> validator =
+            getConstraintValidator(annotation, constraintClasses, owner, access);
+        final AnnotationConstraintBuilder<A> builder =
+            new AnnotationConstraintBuilder<A>(constraintClasses, validator, annotation, owner, access);
+
+        // JSR-303 3.4.4: Add implicit groups
+        if (prop != null && prop.getParentMetaBean() != null) {
+            MetaBean parentMetaBean = prop.getParentMetaBean();
+            // If:
+            // - the owner is an interface
+            // - the class of the metabean being build is different than the
+            // owner
+            // - and only the Default group is defined
+            // Then: add the owner interface as implicit groups
+            if (builder.getConstraintValidation().getOwner().isInterface()
+                && parentMetaBean.getBeanClass() != builder.getConstraintValidation().getOwner()
+                && builder.getConstraintValidation().getGroups().size() == 1
+                && builder.getConstraintValidation().getGroups().contains(Default.class)) {
+                Set<Class<?>> groups = builder.getConstraintValidation().getGroups();
+                groups.add(builder.getConstraintValidation().getOwner());
+                builder.getConstraintValidation().setGroups(groups);
+            }
+        }
+
+        // If already building a constraint composition tree, ensure that:
+        // - the parent groups are inherited
+        // - the parent payload is inherited
+        if (appender instanceof AppendValidationToBuilder) {
+            AppendValidationToBuilder avb = (AppendValidationToBuilder) appender;
+            builder.getConstraintValidation().setGroups(avb.getInheritedGroups());
+            builder.getConstraintValidation().setPayload(avb.getInheritedPayload());
+        }
+
+        // process composed constraints:
+        // here are not other superclasses possible, because annotations do not
+        // inherit!
+        processAnnotations(prop, owner, annotation.annotationType(), access, new AppendValidationToBuilder(builder));
+
+        // Even if the validator is null, it must be added to mimic the RI impl
+        appender.append(builder.getConstraintValidation());
+        return true;
+    }
+
+    private <A extends Annotation, T> ConstraintValidator<A, ? super T> getConstraintValidator(A annotation,
+        Class<? extends ConstraintValidator<A, ?>>[] constraintClasses, Class<?> owner, AccessStrategy access) {
+        if (constraintClasses != null && constraintClasses.length > 0) {
+            Type type = determineTargetedType(owner, access);
+            /**
+             * spec says in chapter 3.5.3.: The ConstraintValidator chosen to
+             * validate a declared type T is the one where the type supported by
+             * the ConstraintValidator is a supertype of T and where there is no
+             * other ConstraintValidator whose supported type is a supertype of
+             * T and not a supertype of the chosen ConstraintValidator supported
+             * type.
+             */
+            Map<Type, Class<? extends ConstraintValidator<A, ?>>> validatorTypes =
+                getValidatorsTypes(constraintClasses);
+            final List<Type> assignableTypes = new ArrayList<Type>(constraintClasses.length);
+            fillAssignableTypes(type, validatorTypes.keySet(), assignableTypes);
+            reduceAssignableTypes(assignableTypes);
+            checkOneType(assignableTypes, type, owner, annotation, access);
+
+            @SuppressWarnings("unchecked")
+            final ConstraintValidator<A, ? super T> validator =
+                (ConstraintValidator<A, ? super T>) factoryContext.getFactory().getConstraintValidatorFactory()
+                    .getInstance(validatorTypes.get(assignableTypes.get(0)));
+            if (validator == null) {
+                throw new ValidationException("Factory returned null validator for: "
+                    + validatorTypes.get(assignableTypes.get(0)));
+
+            }
+            return validator;
+            // NOTE: validator initialization deferred until append phase
+        }
+        return null;
+    }
+
+    private static void checkOneType(List<Type> types, Type targetType, Class<?> owner, Annotation anno,
+        AccessStrategy access) {
+
+        if (types.isEmpty()) {
+            StringBuilder buf =
+                new StringBuilder().append("No validator could be found for type ").append(stringForType(targetType))
+                    .append(". See: @").append(anno.annotationType().getSimpleName()).append(" at ").append(
+                        stringForLocation(owner, access));
+            throw new UnexpectedTypeException(buf.toString());
+        } else if (types.size() > 1) {
+            StringBuilder buf = new StringBuilder();
+            buf.append("Ambiguous validators for type ");
+            buf.append(stringForType(targetType));
+            buf.append(". See: @").append(anno.annotationType().getSimpleName()).append(" at ").append(
+                stringForLocation(owner, access));
+            buf.append(". Validators are: ");
+            boolean comma = false;
+            for (Type each : types) {
+                if (comma)
+                    buf.append(", ");
+                comma = true;
+                buf.append(each);
+            }
+            throw new UnexpectedTypeException(buf.toString());
+        }
+    }
+
+    /** implements spec chapter 3.5.3. ConstraintValidator resolution algorithm. */
+    private static Type determineTargetedType(Class<?> owner, AccessStrategy access) {
+        // if the constraint declaration is hosted on a class or an interface,
+        // the targeted type is the class or the interface.
+        if (access == null)
+            return owner;
+        Type type = access.getJavaType();
+        if (type == null)
+            return Object.class;
+        if (type instanceof Class<?>)
+            type = ClassUtils.primitiveToWrapper((Class<?>) type);
+        return type;
+    }
+
+    private static String stringForType(Type clazz) {
+        if (clazz instanceof Class<?>) {
+            if (((Class<?>) clazz).isArray()) {
+                return ((Class<?>) clazz).getComponentType().getName() + "[]";
+            } else {
+                return ((Class<?>) clazz).getName();
+            }
+        } else {
+            return clazz.toString();
+        }
+    }
+
+    private static String stringForLocation(Class<?> owner, AccessStrategy access) {
+        if (access != null) {
+            return access.toString();
+        } else {
+            return owner.getName();
+        }
+    }
+
+    private static void fillAssignableTypes(Type type, Set<Type> validatorsTypes, List<Type> suitableTypes) {
+        for (Type validatorType : validatorsTypes) {
+            if (org.apache.commons.lang3.reflect.TypeUtils.isAssignable(type, validatorType)
+                && !suitableTypes.contains(validatorType)) {
+                suitableTypes.add(validatorType);
+            }
+        }
+    }
+
+    /**
+     * Tries to reduce all assignable classes down to a single class.
+     * 
+     * @param assignableTypes
+     *            The set of all classes which are assignable to the class of
+     *            the value to be validated and which are handled by at least
+     *            one of the validators for the specified constraint.
+     */
+    private static void reduceAssignableTypes(List<Type> assignableTypes) {
+        if (assignableTypes.size() <= 1) {
+            return; // no need to reduce
+        }
+        boolean removed;
+        do {
+            removed = false;
+            final Type type = assignableTypes.get(0);
+            for (int i = 1; i < assignableTypes.size(); i++) {
+                Type nextType = assignableTypes.get(i);
+                if (TypeUtils.isAssignable(nextType, type)) {
+                    assignableTypes.remove(0);
+                    i--;
+                    removed = true;
+                } else if (TypeUtils.isAssignable(type, nextType)) {
+                    assignableTypes.remove(i--);
+                    removed = true;
+                }
+            }
+        } while (removed && assignableTypes.size() > 1);
+
+    }
+
+    /**
+     * Given a set of {@link ConstraintValidator} implementation classes, map
+     * those to their target types.
+     * 
+     * @param constraintValidatorClasses
+     * @return {@link Map} of {@link Type} : {@link ConstraintValidator} subtype
+     */
+    private static <A extends Annotation> Map<Type, Class<? extends ConstraintValidator<A, ?>>> getValidatorsTypes(
+        Class<? extends ConstraintValidator<A, ?>>[] constraintValidatorClasses) {
+        Map<Type, Class<? extends ConstraintValidator<A, ?>>> validatorsTypes =
+            new HashMap<Type, Class<? extends ConstraintValidator<A, ?>>>();
+        for (Class<? extends ConstraintValidator<A, ?>> validatorType : constraintValidatorClasses) {
+            Type validatedType =
+                TypeUtils.getTypeArguments(validatorType, ConstraintValidator.class).get(
+                    ConstraintValidator.class.getTypeParameters()[1]);
+            if (validatedType == null) {
+                throw new ValidationException(String.format("Could not detect validated type for %s", validatorType));
+            }
+            if (validatedType instanceof GenericArrayType) {
+                Type componentType = TypeUtils.getArrayComponentType(validatedType);
+                if (componentType instanceof Class<?>) {
+                    validatedType = Array.newInstance((Class<?>) componentType, 0).getClass();
+                }
+            }
+            validatorsTypes.put(validatedType, validatorType);
+        }
+        return validatorsTypes;
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheFactoryContext.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheFactoryContext.java
new file mode 100644
index 0000000..8072f59
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheFactoryContext.java
@@ -0,0 +1,306 @@
+/*
+ * 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.bval.jsr303;
+
+import java.lang.reflect.Constructor;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+import javax.validation.ValidationException;
+import javax.validation.Validator;
+import javax.validation.ValidatorContext;
+
+import org.apache.bval.IntrospectorMetaBeanFactory;
+import org.apache.bval.MetaBeanBuilder;
+import org.apache.bval.MetaBeanFactory;
+import org.apache.bval.MetaBeanFinder;
+import org.apache.bval.MetaBeanManager;
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.xml.XMLMetaBeanBuilder;
+import org.apache.bval.xml.XMLMetaBeanFactory;
+import org.apache.bval.xml.XMLMetaBeanManager;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.reflect.ConstructorUtils;
+
+/**
+ * Description: Represents the context that is used to create
+ * <code>ClassValidator</code> instances.<br/>
+ */
+public class ApacheFactoryContext implements ValidatorContext {
+    private final ApacheValidatorFactory factory;
+    private final MetaBeanFinder metaBeanFinder;
+
+    private MessageInterpolator messageInterpolator;
+    private TraversableResolver traversableResolver;
+    private ConstraintValidatorFactory constraintValidatorFactory;
+
+    /**
+     * Create a new ApacheFactoryContext instance.
+     * 
+     * @param factory
+     */
+    public ApacheFactoryContext(ApacheValidatorFactory factory) {
+        this.factory = factory;
+        this.metaBeanFinder = buildMetaBeanFinder();
+    }
+
+    /**
+     * Create a new ApacheFactoryContext instance.
+     * 
+     * @param factory
+     * @param metaBeanFinder
+     */
+    protected ApacheFactoryContext(ApacheValidatorFactory factory, MetaBeanFinder metaBeanFinder) {
+        this.factory = factory;
+        this.metaBeanFinder = metaBeanFinder;
+    }
+
+    /**
+     * Get the {@link ApacheValidatorFactory} used by this
+     * {@link ApacheFactoryContext}.
+     * 
+     * @return {@link ApacheValidatorFactory}
+     */
+    public ApacheValidatorFactory getFactory() {
+        return factory;
+    }
+
+    /**
+     * Get the metaBeanFinder.
+     * 
+     * @return {@link MetaBeanFinder}
+     */
+    public final MetaBeanFinder getMetaBeanFinder() {
+        return metaBeanFinder;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ValidatorContext messageInterpolator(MessageInterpolator messageInterpolator) {
+        this.messageInterpolator = messageInterpolator;
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ValidatorContext traversableResolver(TraversableResolver traversableResolver) {
+        this.traversableResolver = traversableResolver;
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ValidatorContext constraintValidatorFactory(ConstraintValidatorFactory constraintValidatorFactory) {
+        this.constraintValidatorFactory = constraintValidatorFactory;
+        return this;
+    }
+
+    /**
+     * Get the {@link ConstraintValidatorFactory}.
+     * 
+     * @return {@link ConstraintValidatorFactory}
+     */
+    public ConstraintValidatorFactory getConstraintValidatorFactory() {
+        return constraintValidatorFactory == null ? factory.getConstraintValidatorFactory()
+            : constraintValidatorFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Validator getValidator() {
+        ClassValidator validator = new ClassValidator(this);
+        if (Boolean.parseBoolean(factory.getProperties().get(
+            ApacheValidatorConfiguration.Properties.TREAT_MAPS_LIKE_BEANS))) {
+            validator.setTreatMapsLikeBeans(true);
+        }
+        return validator;
+    }
+
+    /**
+     * Get the {@link MessageInterpolator}.
+     * 
+     * @return {@link MessageInterpolator}
+     */
+    public MessageInterpolator getMessageInterpolator() {
+        return messageInterpolator == null ? factory.getMessageInterpolator() : messageInterpolator;
+    }
+
+    /**
+     * Get the {@link TraversableResolver}.
+     * 
+     * @return {@link TraversableResolver}
+     */
+    public TraversableResolver getTraversableResolver() {
+        return traversableResolver == null ? factory.getTraversableResolver() : traversableResolver;
+    }
+
+    /**
+     * Create MetaBeanManager that uses factories:
+     * <ol>
+     * <li>if enabled by
+     * {@link ApacheValidatorConfiguration.Properties#ENABLE_INTROSPECTOR}, an
+     * {@link IntrospectorMetaBeanFactory}</li>
+     * <li>{@link MetaBeanFactory} types (if any) specified by
+     * {@link ApacheValidatorConfiguration.Properties#METABEAN_FACTORY_CLASSNAMES}
+     * </li>
+     * <li>if no {@link Jsr303MetaBeanFactory} has yet been specified (this
+     * allows factory order customization), a {@link Jsr303MetaBeanFactory}
+     * which handles both JSR303-XML and JSR303-Annotations</li>
+     * <li>if enabled by
+     * {@link ApacheValidatorConfiguration.Properties#ENABLE_METABEANS_XML}, an
+     * {@link XMLMetaBeanFactory}</li>
+     * </ol>
+     * 
+     * @return a new instance of MetaBeanManager with adequate MetaBeanFactories
+     */
+    protected MetaBeanFinder buildMetaBeanFinder() {
+        List<MetaBeanFactory> builders = new ArrayList<MetaBeanFactory>();
+        if (Boolean.parseBoolean(factory.getProperties().get(
+            ApacheValidatorConfiguration.Properties.ENABLE_INTROSPECTOR))) {
+            builders.add(new IntrospectorMetaBeanFactory());
+        }
+        String[] factoryClassNames =
+            StringUtils.split(
+                factory.getProperties().get(ApacheValidatorConfiguration.Properties.METABEAN_FACTORY_CLASSNAMES));
+        if (factoryClassNames != null) {
+            for (String clsName : factoryClassNames) {
+                // cast, relying on #createMetaBeanFactory to throw the exception if incompatible:
+                @SuppressWarnings("unchecked")
+                Class<? extends MetaBeanFactory> factoryClass = (Class<? extends MetaBeanFactory>) loadClass(clsName);
+                builders.add(createMetaBeanFactory(factoryClass));
+            }
+        }
+        boolean jsr303Found = false;
+        for (MetaBeanFactory builder : builders) {
+            jsr303Found |= builder instanceof Jsr303MetaBeanFactory;
+        }
+        if (!jsr303Found) {
+            builders.add(new Jsr303MetaBeanFactory(this));
+        }
+        @SuppressWarnings("deprecation")
+        boolean enableMetaBeansXml = Boolean.parseBoolean(factory.getProperties().get(
+            ApacheValidatorConfiguration.Properties.ENABLE_METABEANS_XML));
+        if (enableMetaBeansXml) {
+            XMLMetaBeanManagerCreator.addFactory(builders);
+        }
+        return createMetaBeanManager(builders);
+    }
+
+    /**
+     * Create a {@link MetaBeanManager} using the specified builders.
+     * 
+     * @param builders
+     *            {@link MetaBeanFactory} {@link List}
+     * @return {@link MetaBeanManager}
+     */
+    @SuppressWarnings("deprecation")
+    protected MetaBeanFinder createMetaBeanManager(List<MetaBeanFactory> builders) {
+        // as long as we support both: jsr303 (in the builders list) and xstream-xml metabeans:
+        if (Boolean.parseBoolean(factory.getProperties().get(
+            ApacheValidatorConfiguration.Properties.ENABLE_METABEANS_XML))) {
+            return XMLMetaBeanManagerCreator.createXMLMetaBeanManager(builders);
+        }
+        return new MetaBeanManager(new MetaBeanBuilder(builders.toArray(new MetaBeanFactory[builders.size()])));
+    }
+
+    private <F extends MetaBeanFactory> F createMetaBeanFactory(final Class<F> cls) {
+        return run(new PrivilegedAction<F>() {
+
+            public F run() {
+                try {
+                    Constructor<F> c = ConstructorUtils.getMatchingAccessibleConstructor(cls, ApacheFactoryContext.this.getClass());
+                    if (c != null) {
+                        return c.newInstance(ApacheFactoryContext.this);
+                    }
+                    c = ConstructorUtils.getMatchingAccessibleConstructor(cls, getFactory().getClass());
+                    if (c != null) {
+                        return c.newInstance(getFactory());
+                    }
+                    return cls.newInstance();
+                } catch (Exception e) {
+                    throw new ValidationException(e);
+                }
+            }
+        });
+    }
+
+    /**
+     * separate class to prevent the classloader to immediately load optional
+     * classes: XMLMetaBeanManager, XMLMetaBeanFactory, XMLMetaBeanBuilder that
+     * might not be available in the classpath
+     */
+    private static class XMLMetaBeanManagerCreator {
+
+        static void addFactory(List<MetaBeanFactory> builders) {
+            builders.add(new XMLMetaBeanFactory());
+        }
+
+        /**
+         * Create the {@link MetaBeanManager} to process JSR303 XML. Requires
+         * bval-xstream at RT.
+         * 
+         * @param builders
+         * @return {@link MetaBeanManager}
+         */
+        // NOTE - We return MetaBeanManager instead of XMLMetaBeanManager to
+        // keep
+        // bval-xstream an optional module.
+        protected static MetaBeanManager createXMLMetaBeanManager(List<MetaBeanFactory> builders) {
+            return new XMLMetaBeanManager(
+                new XMLMetaBeanBuilder(builders.toArray(new MetaBeanFactory[builders.size()])));
+        }
+    }
+
+    private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+
+    private Class<?> loadClass(final String className) {
+        ClassLoader loader = doPrivileged(SecureActions.getContextClassLoader());
+        if (loader == null)
+            loader = getClass().getClassLoader();
+
+        try {
+            return Class.forName(className, true, loader);
+        } catch (ClassNotFoundException ex) {
+            throw new ValidationException("Unable to load class: " + className, ex);
+        }
+    }
+
+    private static <T> T run(PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidationProvider.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidationProvider.java
new file mode 100644
index 0000000..286016c
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidationProvider.java
@@ -0,0 +1,130 @@
+/*
+ * 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.bval.jsr303;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import javax.validation.Configuration;
+import javax.validation.ValidationException;
+import javax.validation.ValidatorFactory;
+import javax.validation.spi.BootstrapState;
+import javax.validation.spi.ConfigurationState;
+import javax.validation.spi.ValidationProvider;
+
+import org.apache.commons.lang3.ClassUtils;
+
+/**
+ * Description: Implementation of {@link ValidationProvider} for jsr303
+ * implementation of the apache-validation framework.
+ * <p/>
+ * <br/>
+ * User: roman.stumm <br/>
+ * Date: 29.10.2008 <br/>
+ * Time: 14:45:41 <br/>
+ */
+public class ApacheValidationProvider implements ValidationProvider<ApacheValidatorConfiguration> {
+
+    /**
+     * Learn whether a particular builder class is suitable for this
+     * {@link ValidationProvider}.
+     * 
+     * @param builderClass
+     * @return boolean suitability
+     */
+    public boolean isSuitable(Class<? extends Configuration<?>> builderClass) {
+        return ApacheValidatorConfiguration.class == builderClass;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ApacheValidatorConfiguration createSpecializedConfiguration(BootstrapState state) {
+        return new ConfigurationImpl(state, this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Configuration<?> createGenericConfiguration(BootstrapState state) {
+        return new ConfigurationImpl(state, null);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @throws javax.validation.ValidationException
+     *             if the ValidatorFactory cannot be built
+     */
+    @SuppressWarnings("unchecked")
+    public ValidatorFactory buildValidatorFactory(final ConfigurationState configuration) {
+        final Class<? extends ValidatorFactory> validatorFactoryClass;
+        try {
+            String validatorFactoryClassname =
+                configuration.getProperties().get(ApacheValidatorConfiguration.Properties.VALIDATOR_FACTORY_CLASSNAME);
+
+            if (validatorFactoryClassname == null)
+                validatorFactoryClass = ApacheValidatorFactory.class;
+            else
+            {
+                validatorFactoryClass
+                  = (Class<? extends ValidatorFactory>) ClassUtils.getClass(validatorFactoryClassname);
+                validatorFactoryClass.asSubclass(ValidatorFactory.class);
+            }
+        } catch (ValidationException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            throw new ValidationException("error building ValidatorFactory", ex);
+        }
+
+        // FIXME 2011-03-27 jw:
+        // Should not use privileged action, but to avoid breaking things
+        // doing it here like the former version of this class did.
+        //
+        // The privileged action should be applied by the ValidatorFactory
+        // itself, if required.
+        // No privileges should be required to access the constructor,
+        // because the classloader of ApacheValidationProvider will always
+        // be an ancestor of the loader of validatorFactoryClass.
+        return (System.getSecurityManager() == null)
+            ? instantiateValidatorFactory(validatorFactoryClass, configuration)
+            : AccessController.doPrivileged(new PrivilegedAction<ValidatorFactory>() {
+                  public ValidatorFactory run() {
+                      return instantiateValidatorFactory(validatorFactoryClass, configuration);
+                  }
+              });
+    }
+
+
+
+    private static ValidatorFactory instantiateValidatorFactory(
+        final Class<? extends ValidatorFactory> validatorFactoryClass,
+        final ConfigurationState                configuration
+    ) {
+      try
+      {
+          return validatorFactoryClass.getConstructor(ConfigurationState.class).newInstance(configuration);
+      }
+      catch (final Exception ex)
+      {
+          throw new ValidationException("Cannot instantiate : " + validatorFactoryClass, ex);
+      }
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorConfiguration.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorConfiguration.java
new file mode 100644
index 0000000..8dd3ccf
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorConfiguration.java
@@ -0,0 +1,91 @@
+/*
+ * 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.bval.jsr303;
+
+import javax.validation.Configuration;
+import javax.validation.ValidatorContext;
+import javax.validation.ValidatorFactory;
+import javax.validation.spi.ConfigurationState;
+
+/**
+ * Description: Uniquely identify Apache BVal in the Bean Validation bootstrap
+ * strategy. Also contains Apache BVal specific configurations<br/>
+ */
+public interface ApacheValidatorConfiguration
+      extends Configuration<ApacheValidatorConfiguration> {
+
+    /**
+     * Proprietary property keys for {@link ConfigurationImpl}  
+     */
+    public interface Properties {
+        /**
+         * the location where to look for the validation.xml file.
+         * default: "META-INF/validation.xml"
+         */
+        String VALIDATION_XML_PATH = "apache.bval.validation-xml-path";
+
+        /**
+         * true/false. use Introspector (java beans) metadata additionally
+         * to build metadata with JSR303.<br>
+         * This means that all properties exist that are java-bean properties and
+         * and that some features (Hidden, Readonly) are taken from Introspector
+         * to create the meta data.<br>
+         * default: false
+         */
+        String ENABLE_INTROSPECTOR = "apache.bval.enable-introspector";
+
+        /**
+         * true/false. use Apache metaBeans xml format additionally to
+         * build metadata with JSR303.
+         * default: false
+         *
+         * @deprecated we could decide to drop this feature in the future.
+         * we keep it as long as we support both: jsr303 and xstream-xml meta data at
+         * the same time (and potentially for the same domain classes)
+         */
+        String ENABLE_METABEANS_XML = "apache.bval.enable-metabeans-xml";
+
+        /**
+         * BeanValidator.treatMapsLikeBeans.
+         * default: false
+         */
+        String TREAT_MAPS_LIKE_BEANS = "apache.bval.treat-maps-like-beans";
+
+        /**
+         * Specifies the classname of the {@link ValidatorFactory} to use: this
+         * class is presumed have a constructor that accepts a single
+         * {@link ConfigurationState} argument.
+         */
+         String VALIDATOR_FACTORY_CLASSNAME = "apache.bval.validator-factory-classname";
+
+        /**
+         * Specifies the names, delimited by whitespace, of
+         * {@link MetaBeanFactory} classes that should be added to collaborate
+         * with an {@link ApacheFactoryContext}'s {@link MetaBeanFinder}. These
+         * are instantiated per {@link ValidatorContext}, attempting to use
+         * constructor arguments of decreasing specificity:
+         * <ol>
+         * <li>assignable from the creating {@link ApacheFactoryContext}</li>
+         * <li>assignable from the associated {@link ApacheValidatorFactory}</li>
+         * <li>default (no-args) constructor</li>
+         * </ol>
+         */
+         String METABEAN_FACTORY_CLASSNAMES = "apache.bval.metabean-factory-classnames";
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorFactory.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorFactory.java
new file mode 100644
index 0000000..891efd0
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ApacheValidatorFactory.java
@@ -0,0 +1,390 @@
+/*
+ * 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.bval.jsr303;
+
+import org.apache.bval.jsr303.xml.AnnotationIgnores;
+import org.apache.bval.jsr303.xml.MetaConstraint;
+import org.apache.bval.jsr303.xml.ValidationMappingParser;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.commons.lang3.ClassUtils;
+
+import javax.validation.*;
+import javax.validation.bootstrap.ProviderSpecificBootstrap;
+import javax.validation.spi.ConfigurationState;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+
+/**
+ * Description: a factory is a complete configurated object that can create
+ * validators.<br/>
+ * This instance is not thread-safe.<br/>
+ */
+public class ApacheValidatorFactory implements ValidatorFactory, Cloneable {
+    private static volatile ApacheValidatorFactory DEFAULT_FACTORY;
+    private static final ConstraintDefaults defaultConstraints =
+            new ConstraintDefaults();
+
+    private MessageInterpolator messageResolver;
+    private TraversableResolver traversableResolver;
+    private ConstraintValidatorFactory constraintValidatorFactory;
+    private final Map<String, String> properties;
+
+    /**
+     * information from xml parsing
+     */
+    private final AnnotationIgnores annotationIgnores = new AnnotationIgnores();
+    private final ConstraintCached constraintsCache = new ConstraintCached();
+    private final Map<Class<?>, Class<?>[]> defaultSequences;
+    /**
+     * access strategies for properties with cascade validation @Valid support
+     */
+    private final Map<Class<?>, List<AccessStrategy>> validAccesses;
+    private final Map<Class<?>, List<MetaConstraint<?, ? extends Annotation>>> constraintMap;
+
+    /**
+     * Convenience method to retrieve a default global ApacheValidatorFactory
+     *
+     * @return {@link ApacheValidatorFactory}
+     */
+    public static synchronized ApacheValidatorFactory getDefault() {
+        if (DEFAULT_FACTORY == null) {
+            ProviderSpecificBootstrap<ApacheValidatorConfiguration> provider =
+                    Validation.byProvider(ApacheValidationProvider.class);
+            ApacheValidatorConfiguration configuration = provider.configure();
+            DEFAULT_FACTORY =
+                    (ApacheValidatorFactory) configuration.buildValidatorFactory();
+        }
+        return DEFAULT_FACTORY;
+    }
+
+    /**
+     * Set a particular {@link ApacheValidatorFactory} instance as the default.
+     *
+     * @param aDefaultFactory
+     */
+    public static void setDefault(ApacheValidatorFactory aDefaultFactory) {
+        DEFAULT_FACTORY = aDefaultFactory;
+    }
+
+    /**
+     * Create a new ApacheValidatorFactory instance.
+     */
+    public ApacheValidatorFactory(ConfigurationState configurationState) {
+        properties = new HashMap<String, String>();
+        defaultSequences = new HashMap<Class<?>, Class<?>[]>();
+        validAccesses = new HashMap<Class<?>, List<AccessStrategy>>();
+        constraintMap =
+                new HashMap<Class<?>, List<MetaConstraint<?, ? extends Annotation>>>();
+        configure(configurationState);
+    }
+
+    /**
+     * Configure this {@link ApacheValidatorFactory} from a
+     * {@link ConfigurationState}.
+     *
+     * @param configuration
+     */
+    protected void configure(ConfigurationState configuration) {
+        getProperties().putAll(configuration.getProperties());
+        setMessageInterpolator(configuration.getMessageInterpolator());
+        setTraversableResolver(configuration.getTraversableResolver());
+        setConstraintValidatorFactory(configuration
+                .getConstraintValidatorFactory());
+        ValidationMappingParser parser = new ValidationMappingParser(this);
+        parser.processMappingConfig(configuration.getMappingStreams());
+    }
+
+    /**
+     * Get the property map of this {@link ApacheValidatorFactory}.
+     *
+     * @return Map<String, String>
+     */
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    /**
+     * Get the default {@link MessageInterpolator} used by this
+     * {@link ApacheValidatorFactory}.
+     *
+     * @return {@link MessageInterpolator}
+     */
+    protected MessageInterpolator getDefaultMessageInterpolator() {
+        return messageResolver;
+    }
+
+    /**
+     * Shortcut method to create a new Validator instance with factory's
+     * settings
+     *
+     * @return the new validator instance
+     */
+    public Validator getValidator() {
+        return usingContext().getValidator();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return the validator factory's context
+     */
+    public ApacheFactoryContext usingContext() {
+        return new ApacheFactoryContext(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized ApacheValidatorFactory clone() {
+        try {
+            return (ApacheValidatorFactory) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new InternalError(); // VM bug.
+        }
+    }
+
+    /**
+     * Set the {@link MessageInterpolator} used.
+     *
+     * @param messageResolver
+     */
+    public final void setMessageInterpolator(MessageInterpolator messageResolver) {
+        this.messageResolver = messageResolver;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MessageInterpolator getMessageInterpolator() {
+        return ((messageResolver != null) ? messageResolver
+                : getDefaultMessageInterpolator());
+    }
+
+    /**
+     * Set the {@link TraversableResolver} used.
+     *
+     * @param traversableResolver
+     */
+    public final void setTraversableResolver(
+            TraversableResolver traversableResolver) {
+        this.traversableResolver = traversableResolver;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public TraversableResolver getTraversableResolver() {
+        return traversableResolver;
+    }
+
+    /**
+     * Set the {@link ConstraintValidatorFactory} used.
+     *
+     * @param constraintValidatorFactory
+     */
+    public final void setConstraintValidatorFactory(
+            ConstraintValidatorFactory constraintValidatorFactory) {
+        this.constraintValidatorFactory = constraintValidatorFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintValidatorFactory getConstraintValidatorFactory() {
+        return constraintValidatorFactory;
+    }
+
+    /**
+     * Return an object of the specified type to allow access to the
+     * provider-specific API. If the Bean Validation provider implementation
+     * does not support the specified class, the ValidationException is thrown.
+     *
+     * @param type the class of the object to be returned.
+     * @return an instance of the specified class
+     * @throws ValidationException if the provider does not support the call.
+     */
+    public <T> T unwrap(final Class<T> type) {
+        // FIXME 2011-03-27 jw:
+        // This code is unsecure.
+        // It should allow only a fixed set of classes.
+        // Can't fix this because don't know which classes this method should support.
+
+        if (type.isInstance(this)) {
+            @SuppressWarnings("unchecked")
+            T result = (T) this;
+            return result;
+        } else if (!(type.isInterface() || Modifier.isAbstract(type
+                .getModifiers()))) {
+            return newInstance(type);
+        } else {
+            try {
+                Class<?> cls = ClassUtils.getClass(type.getName() + "Impl");
+                if (type.isAssignableFrom(cls)) {
+                    @SuppressWarnings("unchecked")
+                    T result = (T) newInstance(cls);
+                    return result;
+                }
+            } catch (ClassNotFoundException e) {
+                // do nothing
+            }
+            throw new ValidationException("Type " + type + " not supported");
+        }
+    }
+
+    private <T> T newInstance(final Class<T> cls) {
+        return AccessController.doPrivileged(new PrivilegedAction<T>() {
+            public T run() {
+                try {
+                    return cls.newInstance();
+                } catch (final Exception ex) {
+                    throw new ValidationException("Cannot instantiate : " + cls, ex);
+                }
+            }
+        });
+    }
+
+    /**
+     * Get the detected {@link ConstraintDefaults}.
+     *
+     * @return ConstraintDefaults
+     */
+    public ConstraintDefaults getDefaultConstraints() {
+        return defaultConstraints;
+    }
+
+    /**
+     * Get the detected {@link AnnotationIgnores}.
+     *
+     * @return AnnotationIgnores
+     */
+    public AnnotationIgnores getAnnotationIgnores() {
+        return annotationIgnores;
+    }
+
+    /**
+     * Get the constraint cache used.
+     *
+     * @return {@link ConstraintCached}
+     */
+    public ConstraintCached getConstraintsCache() {
+        return constraintsCache;
+    }
+
+    /**
+     * Add a meta-constraint to this {@link ApacheValidatorFactory}'s runtime
+     * customizations.
+     *
+     * @param beanClass
+     * @param metaConstraint
+     */
+    public void addMetaConstraint(Class<?> beanClass,
+                                  MetaConstraint<?, ?> metaConstraint) {
+        List<MetaConstraint<?, ? extends Annotation>> slot =
+                constraintMap.get(beanClass);
+        if (slot != null) {
+            slot.add(metaConstraint);
+        } else {
+            List<MetaConstraint<?, ? extends Annotation>> constraintList =
+                    new ArrayList<MetaConstraint<?, ? extends Annotation>>();
+            constraintList.add(metaConstraint);
+            constraintMap.put(beanClass, constraintList);
+        }
+    }
+
+    /**
+     * Mark a property of <code>beanClass</code> for nested validation.
+     *
+     * @param beanClass
+     * @param accessStrategy
+     *            defining the property to validate
+     */
+    public void addValid(Class<?> beanClass, AccessStrategy accessStrategy) {
+        List<AccessStrategy> slot = validAccesses.get(beanClass);
+        if (slot != null) {
+            slot.add(accessStrategy);
+        } else {
+            List<AccessStrategy> tmpList = new ArrayList<AccessStrategy>();
+            tmpList.add(accessStrategy);
+            validAccesses.put(beanClass, tmpList);
+        }
+    }
+
+    /**
+     * Set the default group sequence for a particular bean class.
+     *
+     * @param beanClass
+     * @param groupSequence
+     */
+    public void addDefaultSequence(Class<?> beanClass, Class<?>[] groupSequence) {
+        defaultSequences.put(beanClass, groupSequence);
+    }
+
+    /**
+     * Retrieve the runtime constraint configuration for a given class.
+     *
+     * @param <T>
+     * @param beanClass
+     * @return List of {@link MetaConstraint}s applicable to
+     *         <code>beanClass</code>
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public <T> List<MetaConstraint<T, ? extends Annotation>> getMetaConstraints(
+            Class<T> beanClass) {
+        List<MetaConstraint<?, ? extends Annotation>> slot =
+                constraintMap.get(beanClass);
+        if (slot != null) {
+            // noinspection RedundantCast
+            return (List) slot;
+        } else {
+            return Collections.EMPTY_LIST;
+        }
+    }
+
+    /**
+     * Get the {@link AccessStrategy} {@link List} indicating nested bean
+     * validations that must be triggered in the course of validating a
+     * <code>beanClass</code> graph.
+     *
+     * @param beanClass
+     * @return {@link List} of {@link AccessStrategy}
+     */
+    public List<AccessStrategy> getValidAccesses(Class<?> beanClass) {
+        List<AccessStrategy> slot = validAccesses.get(beanClass);
+        if (slot != null) {
+            return slot;
+        } else {
+            return Collections.<AccessStrategy>emptyList();
+        }
+    }
+
+    /**
+     * Get the default group sequence configured for <code>beanClass</code>.
+     *
+     * @param beanClass
+     * @return group Class array
+     */
+    public Class<?>[] getDefaultSequence(Class<?> beanClass) {
+        return defaultSequences.get(beanClass);
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidation.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidation.java
new file mode 100644
index 0000000..8d7dba4
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidation.java
@@ -0,0 +1,32 @@
+/*
+ *  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.bval.jsr303;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * Description: unified interface to accumulate {@link ConstraintValidation}s
+ * to varied targets.<br/>
+ */
+public interface AppendValidation {
+    /**
+     * Append a {@link ConstraintValidation}.
+     * @param <T>
+     * @param validation
+     */
+    <T extends Annotation> void append(ConstraintValidation<T> validation);
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToBuilder.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToBuilder.java
new file mode 100644
index 0000000..d112251
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToBuilder.java
@@ -0,0 +1,89 @@
+/*
+ *  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.bval.jsr303;
+
+import org.apache.bval.jsr303.xml.AnnotationProxyBuilder;
+
+import javax.validation.Payload;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+
+/**
+ * Description: Adapt {@link AnnotationConstraintBuilder} to the {@link AppendValidation} interface.<br/>
+ */
+public class AppendValidationToBuilder extends BaseAppendValidation {
+    private final AnnotationConstraintBuilder<?> builder;
+
+    /**
+     * Create a new AppendValidationToBuilder instance.
+     * @param builder
+     */
+    public AppendValidationToBuilder(AnnotationConstraintBuilder<?> builder) {
+        this.builder = builder;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T extends Annotation> void preProcessValidation(ConstraintValidation<T> validation) {
+        // JSR-303 2.3:
+        // Groups from the main constraint annotation are inherited by the composing annotations.
+        // Any groups definition on a composing annotation is ignored.
+        Set<Class<?>> inheritedGroups = builder.getConstraintValidation().getGroups();
+        validation.setGroups(inheritedGroups);
+        
+        // JSR-303 2.3 p:
+        // Payloads are also inherited
+        Set<Class<? extends Payload>> inheritedPayload = builder.getConstraintValidation().getPayload();
+        validation.setPayload(inheritedPayload);
+
+        // Inherited groups and payload values must also be replicated in the 
+        // annotation, so it has to be substituted with a new proxy.
+        T originalAnnot = validation.getAnnotation();
+        AnnotationProxyBuilder<T> apb = new AnnotationProxyBuilder<T>(originalAnnot);
+        apb.putValue(ConstraintAnnotationAttributes.GROUPS.getAttributeName(),
+            inheritedGroups.toArray(new Class[inheritedGroups.size()]));
+        apb.putValue(ConstraintAnnotationAttributes.PAYLOAD.getAttributeName(),
+            inheritedPayload.toArray(new Class[inheritedPayload.size()]));
+        T newAnnot = apb.createAnnotation();
+        validation.setAnnotation(newAnnot);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T extends Annotation> void performAppend(ConstraintValidation<T> validation) {
+        builder.addComposed(validation);
+    }
+
+    /**
+     * Get inherited groups.
+     * @return The set of groups from the parent constraint.
+     */
+    public Set<Class<?>> getInheritedGroups() {
+        return builder.getConstraintValidation().getGroups();
+    }
+
+    /**
+     * Get inherited payload.
+     * @return The set of payloads from the parent constraint.
+     */
+    public Set<Class<? extends Payload>> getInheritedPayload() {
+        return builder.getConstraintValidation().getPayload();
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToMeta.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToMeta.java
new file mode 100644
index 0000000..e871289
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/AppendValidationToMeta.java
@@ -0,0 +1,43 @@
+/*
+ *  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.bval.jsr303;
+
+import org.apache.bval.model.FeaturesCapable;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * Description: adapt any {@link FeaturesCapable} from the core meta-model to the {@link AppendValidation} interface.<br/>
+ */
+public class AppendValidationToMeta extends BaseAppendValidation {
+    private final FeaturesCapable feature;
+
+    /**
+     * Create a new AppendValidationToMeta instance.
+     * @param meta
+     */
+    public AppendValidationToMeta(FeaturesCapable meta) {
+        this.feature = meta;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T extends Annotation> void performAppend(ConstraintValidation<T> validation) {
+        feature.addValidation(validation);
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/BaseAppendValidation.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/BaseAppendValidation.java
new file mode 100644
index 0000000..cb214a3
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/BaseAppendValidation.java
@@ -0,0 +1,80 @@
+/*
+ * 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.bval.jsr303;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * Abstract base validation appender that initializes the
+ * {@link ConstraintValidation#getValidator()} on post-processing.
+ * 
+ * @author Carlos Vara
+ */
+public abstract class BaseAppendValidation implements AppendValidation {
+
+    /**
+     * {@inheritDoc}
+     *
+     * Append operation divided in 3 stages: pre & post processing and the
+     * "real" append process.
+     */
+    public final <T extends Annotation> void append(ConstraintValidation<T> validation) {
+        preProcessValidation(validation);
+        performAppend(validation);
+        postProcessValidation(validation);
+    }
+
+    /**
+     * Performs the actual "appending" operation to the underlying data
+     * structure that holds the validations. Implementations shouldn't try to do
+     * more than that in this step.
+     * 
+     * @param <T>
+     *            The type of the validation.
+     * @param validation
+     *            The validation to be appended.
+     */
+    public abstract <T extends Annotation> void performAppend(ConstraintValidation<T> validation);
+
+    /**
+     * Pre-process the validation before appending it.
+     * 
+     * @param <T>
+     *            The type of the validation.
+     * @param validation
+     *            The validation to be appended.
+     */
+    public <T extends Annotation> void preProcessValidation(ConstraintValidation<T> validation) {
+        // No generic pre-processing
+    }
+    
+    /**
+     * Post-process the validation once it has been appended.
+     * 
+     * @param <T>
+     *            The type of the validation.
+     * @param validation
+     *            The validation to be appended.
+     */
+    public <T extends Annotation> void postProcessValidation(ConstraintValidation<T> validation) {
+        // Initialize the validator
+        validation.initialize();
+    }
+    
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/BeanDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/BeanDescriptorImpl.java
new file mode 100644
index 0000000..1bf500b
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/BeanDescriptorImpl.java
@@ -0,0 +1,136 @@
+/*
+ * 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.bval.jsr303;
+
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.PropertyDescriptor;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Description: Implements {@link BeanDescriptor}.<br/>
+ */
+public class BeanDescriptorImpl extends ElementDescriptorImpl implements BeanDescriptor {
+    /**
+     * The {@link ApacheFactoryContext} (not) used by this
+     * {@link BeanDescriptorImpl}
+     */
+    protected final ApacheFactoryContext factoryContext;
+
+    /**
+     * Create a new BeanDescriptorImpl instance.
+     * 
+     * @param factoryContext
+     * @param metaBean
+     */
+    protected BeanDescriptorImpl(ApacheFactoryContext factoryContext, MetaBean metaBean) {
+        super(metaBean, metaBean.getBeanClass(), metaBean.getValidations());
+        this.factoryContext = factoryContext;
+    }
+
+    /**
+     * Returns true if the bean involves validation:
+     * <ul>
+     * <li>a constraint is hosted on the bean itself</li>
+     * <li>a constraint is hosted on one of the bean properties, OR</li>
+     * <li>a bean property is marked for cascade (<code>@Valid</code>)</li>
+     * </ul>
+     * 
+     * @return true if the bean involves validation
+     */
+    public boolean isBeanConstrained() {
+        if (hasAnyConstraints())
+            return true;
+        for (MetaProperty mprop : metaBean.getProperties()) {
+            if (mprop.getMetaBean() != null || mprop.getFeature(Features.Property.REF_CASCADE) != null)
+                return true;
+        }
+        return false;
+    }
+
+    private boolean hasAnyConstraints() {
+        if (hasConstraints())
+            return true;
+        for (MetaProperty mprop : metaBean.getProperties()) {
+            if (getConstraintDescriptors(mprop.getValidations()).size() > 0)
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Return the property level constraints for a given propertyName or {@code null} if
+     * either the property does not exist or has no constraint. The returned
+     * object (and associated objects including ConstraintDescriptors) are
+     * immutable.
+     * 
+     * @param propertyName
+     *            property evaluated
+     */
+    public PropertyDescriptor getConstraintsForProperty(String propertyName) {
+        if (propertyName == null || propertyName.trim().length() == 0) {
+            throw new IllegalArgumentException("propertyName cannot be null or empty");
+        }
+        MetaProperty prop = metaBean.getProperty(propertyName);
+        if (prop == null)
+            return null;
+        // If no constraints and not cascaded, return null
+        if (prop.getValidations().length == 0 && prop.getFeature(Features.Property.REF_CASCADE) == null) {
+            return null;
+        }
+        return getPropertyDescriptor(prop);
+    }
+
+    private PropertyDescriptor getPropertyDescriptor(MetaProperty prop) {
+        PropertyDescriptorImpl edesc = prop.getFeature(Jsr303Features.Property.PropertyDescriptor);
+        if (edesc == null) {
+            edesc = new PropertyDescriptorImpl(prop);
+            prop.putFeature(Jsr303Features.Property.PropertyDescriptor, edesc);
+        }
+        return edesc;
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @return the property descriptors having at least a constraint defined
+     */
+    public Set<PropertyDescriptor> getConstrainedProperties() {
+        Set<PropertyDescriptor> validatedProperties = new HashSet<PropertyDescriptor>();
+        for (MetaProperty prop : metaBean.getProperties()) {
+            if (prop.getValidations().length > 0
+                || (prop.getMetaBean() != null || prop.getFeature(Features.Property.REF_CASCADE) != null)) {
+                validatedProperties.add(getPropertyDescriptor(prop));
+            }
+        }
+        return Collections.unmodifiableSet(validatedProperties);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return "BeanDescriptorImpl{" + "returnType=" + elementClass + '}';
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/CascadingPropertyValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/CascadingPropertyValidator.java
new file mode 100644
index 0000000..a830960
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/CascadingPropertyValidator.java
@@ -0,0 +1,69 @@
+/**
+ *  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.bval.jsr303;
+
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Valid;
+import javax.validation.Validator;
+
+/**
+ * Per the bean validation spec, {@link Valid} is not honored by the
+ * {@link #validateProperty(Object, String, Class...)} and
+ * {@link #validateValue(Class, String, Object, Class...)} methods. The
+ * {@link CascadingPropertyValidator} interface thus defines a {@link Validator} that
+ * provides corresponding methods that <em>may</em> honor {@link Valid}.
+ * It should be noted that {@link Validator#validateProperty(Object, String, Class...)}
+ * and {@link Validator#validateValue(Class, String, Object, Class...)} are assumed
+ * semantically equivalent to calling the {@link CascadingPropertyValidator}-defined
+ * methods with <code>cascade == false</code>.
+ * 
+ * @version $Rev: 993539 $ $Date: 2010-09-07 16:27:50 -0500 (Tue, 07 Sep 2010) $
+ */
+public interface CascadingPropertyValidator extends Validator {
+
+    /**
+     * Validates all constraints placed on <code>object</code>'s
+     * <code>propertyName</code> property, with optional validation cascading.
+     * 
+     * @param <T>
+     * @param object
+     * @param propertyName
+     * @param cascade
+     * @param groups
+     * @return the resulting {@link Set} of {@link ConstraintViolation}s.
+     */
+    <T extends Object> java.util.Set<javax.validation.ConstraintViolation<T>> validateProperty(T object,
+        String propertyName, boolean cascade, java.lang.Class<?>... groups);
+
+    /**
+     * Validates all constraints placed on <code>object</code>'s
+     * <code>propertyName</code> property, with optional validation cascading,
+     * given a hypothetical property <code>value</code>.
+     * 
+     * @param <T>
+     * @param beanType
+     * @param propertyName
+     * @param value
+     * @param cascade
+     * @param groups
+     * @return the resulting {@link Set} of {@link ConstraintViolation}s.
+     */
+    <T extends Object> java.util.Set<javax.validation.ConstraintViolation<T>> validateValue(
+        java.lang.Class<T> beanType, String propertyName, Object value, boolean cascade, java.lang.Class<?>... groups);
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java
new file mode 100644
index 0000000..571c956
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ClassValidator.java
@@ -0,0 +1,817 @@
+/*
+ * 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.bval.jsr303;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ValidationException;
+import javax.validation.groups.Default;
+import javax.validation.metadata.BeanDescriptor;
+
+import org.apache.bval.DynamicMetaBean;
+import org.apache.bval.MetaBeanFinder;
+import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.groups.Groups;
+import org.apache.bval.jsr303.groups.GroupsComputer;
+import org.apache.bval.jsr303.util.ClassHelper;
+import org.apache.bval.jsr303.util.NodeImpl;
+import org.apache.bval.jsr303.util.PathImpl;
+import org.apache.bval.jsr303.util.PathNavigation;
+import org.apache.bval.jsr303.util.ValidationContextTraversal;
+import org.apache.bval.model.Features;
+import org.apache.bval.model.FeaturesCapable;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.ValidationHelper;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+// TODO: centralize treatMapsLikeBeans
+
+/**
+ * Objects of this class are able to validate bean instances (and the associated object graphs).
+ * <p/>
+ * Implementation is thread-safe.
+ * <p/>
+ * API class
+ *
+ * @version $Rev$ $Date$
+ * 
+ * @author Roman Stumm
+ * @author Carlos Vara
+ */
+public class ClassValidator implements CascadingPropertyValidator {
+    private static final Object VALIDATE_PROPERTY = new Object() {
+        public String toString() {
+            return "VALIDATE_PROPERTY";
+        }
+    };
+
+    /**
+     * {@link ApacheFactoryContext} used
+     */
+    protected final ApacheFactoryContext factoryContext;
+
+    /**
+     * {@link GroupsComputer} used
+     */
+    protected final GroupsComputer groupsComputer = new GroupsComputer();
+
+    /**
+     * Create a new ClassValidator instance.
+     *
+     * @param factoryContext
+     */
+    public ClassValidator(ApacheFactoryContext factoryContext) {
+        this.factoryContext = factoryContext;
+    }
+
+    /**
+     * Create a new ClassValidator instance.
+     *
+     * @param factory
+     * @deprecated provided for backward compatibility
+     */
+    public ClassValidator(ApacheValidatorFactory factory) {
+        this(factory.usingContext());
+    }
+
+    /**
+     * Get the metabean finder associated with this validator.
+     *
+     * @return a MetaBeanFinder
+     * @see org.apache.bval.MetaBeanManagerFactory#getFinder()
+     */
+    protected MetaBeanFinder getMetaBeanFinder() {
+        return factoryContext.getMetaBeanFinder();
+    }
+
+    // Validator implementation
+    // --------------------------------------------------
+
+    /**
+     * {@inheritDoc} Validates all constraints on <code>object</code>.
+     *
+     * @param object object to validate
+     * @param groups group or list of groups targeted for validation (default to
+     *               {@link javax.validation.groups.Default})
+     * @return constraint violations or an empty Set if none
+     * @throws IllegalArgumentException if object is null or if null is passed to the varargs groups
+     * @throws ValidationException      if a non recoverable error happens during the validation
+     *                                  process
+     */
+    // @Override - not allowed in 1.5 for Interface methods
+    @SuppressWarnings("unchecked")
+    public <T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups) {
+        if (object == null)
+            throw new IllegalArgumentException("cannot validate null");
+        checkGroups(groups);
+
+        try {
+
+            Class<T> objectClass = (Class<T>) object.getClass();
+            MetaBean objectMetaBean = getMetaBeanFinder().findForClass(objectClass);
+
+            final GroupValidationContext<T> context = createContext(objectMetaBean, object, objectClass, groups);
+            final ConstraintValidationListener<T> result = context.getListener();
+            final Groups sequence = context.getGroups();
+
+            // 1. process groups
+            for (Group current : sequence.getGroups()) {
+                context.setCurrentGroup(current);
+                validateBeanNet(context);
+            }
+
+            // 2. process sequences
+            for (List<Group> eachSeq : sequence.getSequences()) {
+                for (Group current : eachSeq) {
+                    context.setCurrentGroup(current);
+                    validateBeanNet(context);
+                    // if one of the group process in the sequence leads to one
+                    // or more validation failure,
+                    // the groups following in the sequence must not be
+                    // processed
+                    if (!result.isEmpty())
+                        break;
+                }
+                if (!result.isEmpty())
+                    break;
+            }
+            return result.getConstraintViolations();
+        } catch (RuntimeException ex) {
+            throw unrecoverableValidationError(ex, object);
+        }
+    }
+
+    /**
+     * {@inheritDoc} Validates all constraints placed on the property of <code>object</code> named
+     * <code>propertyName</code>.
+     *
+     * @param object       object to validate
+     * @param propertyName property to validate (ie field and getter constraints). Nested
+     *                     properties may be referenced (e.g. prop[2].subpropA.subpropB)
+     * @param groups       group or list of groups targeted for validation (default to
+     *                     {@link javax.validation.groups.Default})
+     * @return constraint violations or an empty Set if none
+     * @throws IllegalArgumentException if <code>object</code> is null, if <code>propertyName</code>
+     *                                  null, empty or not a valid object property or if null is
+     *                                  passed to the varargs groups
+     * @throws ValidationException      if a non recoverable error happens during the validation
+     *                                  process
+     */
+    // @Override - not allowed in 1.5 for Interface methods
+    public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, Class<?>... groups) {
+        return validateProperty(object, propertyName, false, groups);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> Set<ConstraintViolation<T>> validateProperty(T object, String propertyName, boolean cascade,
+        Class<?>... groups) {
+
+        if (object == null)
+            throw new IllegalArgumentException("cannot validate null");
+
+        @SuppressWarnings("unchecked")
+        Set<ConstraintViolation<T>> result =
+            validateValueImpl((Class<T>) object.getClass(), object, propertyName, VALIDATE_PROPERTY, cascade, groups);
+        return result;
+    }
+
+    /**
+     * {@inheritDoc} Validates all constraints placed on the property named <code>propertyName</code> of the class
+     * <code>beanType</code> would the property value be <code>value</code>
+     * <p/>
+     * <code>ConstraintViolation</code> objects return null for {@link ConstraintViolation#getRootBean()} and
+     * {@link ConstraintViolation#getLeafBean()}
+     *
+     * @param beanType     the bean type
+     * @param propertyName property to validate
+     * @param value        property value to validate
+     * @param groups       group or list of groups targeted for validation (default to
+     *                     {@link javax.validation.groups.Default})
+     * @return constraint violations or an empty Set if none
+     * @throws IllegalArgumentException if <code>beanType</code> is null, if
+     *                                  <code>propertyName</code> null, empty or not a valid object
+     *                                  property or if null is passed to the varargs groups
+     * @throws ValidationException      if a non recoverable error happens during the validation
+     *                                  process
+     */
+    // @Override - not allowed in 1.5 for Interface methods
+    public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value,
+        Class<?>... groups) {
+        return validateValue(beanType, propertyName, value, false, groups);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> Set<ConstraintViolation<T>> validateValue(Class<T> beanType, String propertyName, Object value,
+        boolean cascade, Class<?>... groups) {
+        return validateValueImpl(checkBeanType(beanType), null, propertyName, value, cascade, groups);
+    }
+
+    /**
+     * {@inheritDoc} Return the descriptor object describing bean constraints. The returned object (and associated
+     * objects including <code>ConstraintDescriptor<code>s) are immutable.
+     *
+     * @param clazz class or interface type evaluated
+     * @return the bean descriptor for the specified class.
+     * @throws IllegalArgumentException if clazz is null
+     * @throws ValidationException      if a non recoverable error happens during the metadata
+     *                                  discovery or if some constraints are invalid.
+     */
+    // @Override - not allowed in 1.5 for Interface methods
+    public BeanDescriptor getConstraintsForClass(Class<?> clazz) {
+        if (clazz == null) {
+            throw new IllegalArgumentException("Class cannot be null");
+        }
+        try {
+            MetaBean metaBean = getMetaBeanFinder().findForClass(clazz);
+            BeanDescriptorImpl edesc = metaBean.getFeature(Jsr303Features.Bean.BEAN_DESCRIPTOR);
+            if (edesc == null) {
+                edesc = createBeanDescriptor(metaBean);
+                metaBean.putFeature(Jsr303Features.Bean.BEAN_DESCRIPTOR, edesc);
+            }
+            return edesc;
+        } catch (RuntimeException ex) {
+            throw new ValidationException("error retrieving constraints for " + clazz, ex);
+        }
+    }
+
+    /**
+     * {@inheritDoc} Return an instance of the specified type allowing access to provider-specific APIs. If the Bean
+     * Validation provider implementation does not support the specified class, <code>ValidationException</code> is
+     * thrown.
+     *
+     * @param type the class of the object to be returned.
+     * @return an instance of the specified class
+     * @throws ValidationException if the provider does not support the call.
+     */
+    // @Override - not allowed in 1.5 for Interface methods
+    public <T> T unwrap(Class<T> type) {
+        // FIXME 2011-03-27 jw:
+        // This code is unsecure.
+        // It should allow only a fixed set of classes.
+        // Can't fix this because don't know which classes this method should support.
+
+        if (type.isAssignableFrom(getClass())) {
+            @SuppressWarnings("unchecked")
+            final T result = (T) this;
+            return result;
+        } else if (!(type.isInterface() || Modifier.isAbstract(type.getModifiers()))) {
+            return newInstance(type);
+        } else {
+            try {
+                Class<?> cls = ClassUtils.getClass(type.getName() + "Impl");
+                if (type.isAssignableFrom(cls)) {
+                    @SuppressWarnings("unchecked")
+                    final Class<? extends T> implClass = (Class<? extends T>) cls;
+                    return newInstance(implClass);
+                }
+            } catch (ClassNotFoundException e) {
+            }
+            throw new ValidationException("Type " + type + " not supported");
+        }
+    }
+
+    private <T> T newInstance(final Class<T> cls) {
+        return AccessController.doPrivileged(new PrivilegedAction<T>() {
+            public T run() {
+                try {
+                    Constructor<T> cons = cls.getConstructor(ApacheFactoryContext.class);
+                    if (!cons.isAccessible()) {
+                        cons.setAccessible(true);
+                    }
+                    return cons.newInstance(factoryContext);
+                } catch (final Exception ex) {
+                    throw new ValidationException("Cannot instantiate : " + cls, ex);
+                }
+            }
+        });
+    }
+
+    // Helpers
+    // -------------------------------------------------------------------
+
+    /**
+     * Validates a bean and all its cascaded related beans for the currently defined group.
+     * <p/>
+     * Special code is present to manage the {@link Default} group.
+     *
+     * @param validationContext The current context of this validation call. Must have its
+     *                          {@link GroupValidationContext#getCurrentGroup()} field set.
+     */
+    protected void validateBeanNet(GroupValidationContext<?> context) {
+
+        // If reached a cascaded bean which is null
+        if (context.getBean() == null) {
+            return;
+        }
+
+        // If reached a cascaded bean which has already been validated for the
+        // current group
+        if (!context.collectValidated()) {
+            return;
+        }
+
+        // ### First, validate the bean
+
+        // Default is a special case
+        if (context.getCurrentGroup().isDefault()) {
+
+            List<Group> defaultGroups = expandDefaultGroup(context);
+            final ConstraintValidationListener<?> result = (ConstraintValidationListener<?>) context.getListener();
+
+            // If the rootBean defines a GroupSequence
+            if (defaultGroups != null && defaultGroups.size() > 1) {
+
+                int numViolations = result.violationsSize();
+
+                // Validate the bean for each group in the sequence
+                Group currentGroup = context.getCurrentGroup();
+                for (Group each : defaultGroups) {
+                    context.setCurrentGroup(each);
+                    ValidationHelper.validateBean(context);
+                    // Spec 3.4.3 - Stop validation if errors already found
+                    if (result.violationsSize() > numViolations) {
+                        break;
+                    }
+                }
+                context.setCurrentGroup(currentGroup);
+            } else {
+
+                // For each class in the hierarchy of classes of rootBean,
+                // validate the constraints defined in that class according
+                // to the GroupSequence defined in the same class
+
+                // Obtain the full class hierarchy
+                List<Class<?>> classHierarchy = new ArrayList<Class<?>>();
+                ClassHelper.fillFullClassHierarchyAsList(classHierarchy, context.getMetaBean().getBeanClass());
+                Class<?> initialOwner = context.getCurrentOwner();
+
+                // For each owner in the hierarchy
+                for (Class<?> owner : classHierarchy) {
+                    context.setCurrentOwner(owner);
+
+                    int numViolations = result.violationsSize();
+
+                    // Obtain the group sequence of the owner, and use it for
+                    // the constraints that belong to it
+                    List<Group> ownerDefaultGroups =
+                        context.getMetaBean().getFeature("{GroupSequence:" + owner.getCanonicalName() + "}");
+                    for (Group each : ownerDefaultGroups) {
+                        context.setCurrentGroup(each);
+                        ValidationHelper.validateBean(context);
+                        // Spec 3.4.3 - Stop validation if errors already found
+                        if (result.violationsSize() > numViolations) {
+                            break;
+                        }
+                    }
+
+                }
+                context.setCurrentOwner(initialOwner);
+                context.setCurrentGroup(Group.DEFAULT);
+
+            }
+
+        }
+        // if not the default group, proceed as normal
+        else {
+            ValidationHelper.validateBean(context);
+        }
+
+        // ### Then, the cascaded beans (@Valid)
+        for (MetaProperty prop : context.getMetaBean().getProperties()) {
+            validateCascadedBean(context, prop);
+        }
+
+    }
+
+    /**
+     * Checks if the the meta property <code>prop</code> defines a cascaded bean, and in case it does, validates it.
+     *
+     * @param context The current validation context.
+     * @param prop    The property to cascade from (in case it is possible).
+     */
+    private void validateCascadedBean(GroupValidationContext<?> context, MetaProperty prop) {
+        AccessStrategy[] access = prop.getFeature(Features.Property.REF_CASCADE);
+        if (access != null) { // different accesses to relation
+            // save old values from context
+            final Object bean = context.getBean();
+            final MetaBean mbean = context.getMetaBean();
+            // TODO implement Validation.groups support on related bean
+//            Class[] groups = prop.getFeature(Jsr303Features.Property.REF_GROUPS);
+            for (AccessStrategy each : access) {
+                if (isCascadable(context, prop, each)) {
+                    // modify context state for relationship-target bean
+                    context.moveDown(prop, each);
+                    // Now, if the related bean is an instance of Map/Array/etc,
+                    ValidationHelper
+                        .validateContext(context, new Jsr303ValidationCallback(context), treatMapsLikeBeans);
+                    // restore old values in context
+                    context.moveUp(bean, mbean);
+                }
+            }
+        }
+    }
+
+    /**
+     * Before accessing a related bean (marked with {@link javax.validation.Valid}), the validator has to check if it is
+     * reachable and cascadable.
+     *
+     * @param context The current validation context.
+     * @param prop    The property of the related bean.
+     * @param access  The access strategy used to get the related bean value.
+     * @return <code>true</code> if the validator can access the related bean, <code>false</code> otherwise.
+     */
+    private boolean isCascadable(GroupValidationContext<?> context, MetaProperty prop, AccessStrategy access) {
+
+        PathImpl beanPath = context.getPropertyPath();
+        NodeImpl node = new NodeImpl(prop.getName());
+        if (beanPath == null) {
+            beanPath = PathImpl.create(null);
+        }
+        try {
+            if (!context.getTraversableResolver().isReachable(context.getBean(), node,
+                context.getRootMetaBean().getBeanClass(), beanPath, access.getElementType()))
+                return false;
+        } catch (RuntimeException e) {
+            throw new ValidationException("Error in TraversableResolver.isReachable() for " + context.getBean(), e);
+        }
+
+        try {
+            if (!context.getTraversableResolver().isCascadable(context.getBean(), node,
+                context.getRootMetaBean().getBeanClass(), beanPath, access.getElementType()))
+                return false;
+        } catch (RuntimeException e) {
+            throw new ValidationException("Error TraversableResolver.isCascadable() for " + context.getBean(), e);
+        }
+
+        return true;
+    }
+
+    /**
+     * in case of a default group return the list of groups for a redefined default GroupSequence
+     *
+     * @return null when no in default group or default group sequence not redefined
+     */
+    private List<Group> expandDefaultGroup(GroupValidationContext<?> context) {
+        if (context.getCurrentGroup().isDefault()) {
+            // mention if metaBean redefines the default group
+            List<Group> groupSeq = context.getMetaBean().getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
+            if (groupSeq != null) {
+                context.getGroups().assertDefaultGroupSequenceIsExpandable(groupSeq);
+            }
+            return groupSeq;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Generate an unrecoverable validation error
+     *
+     * @param ex
+     * @param object
+     * @return a {@link RuntimeException} of the appropriate type
+     */
+    @SuppressWarnings("finally")
+    protected static RuntimeException unrecoverableValidationError(RuntimeException ex, Object object) {
+        if (ex instanceof UnknownPropertyException || ex instanceof IncompatiblePropertyValueException) {
+            // Convert to IllegalArgumentException
+            return new IllegalArgumentException(ex.getMessage(), ex);
+        } else if (ex instanceof ValidationException) {
+            return ex; // do not wrap specific ValidationExceptions (or
+            // instances from subclasses)
+        } else {
+            String objectId = "";
+            try {
+                if (object != null) {
+                    objectId = object.toString();
+                } else {
+                    objectId = "<null>";
+                }
+            } catch (Exception e) {
+                objectId = "<unknown>";
+            } finally {
+                return new ValidationException("error during validation of "
+                        + objectId, ex);
+            }
+        }
+    }
+
+    private void validatePropertyInGroup(final GroupValidationContext<?> context) {
+        final Runnable helper;
+        if (context.getMetaProperty() == null) {
+            helper = new Runnable() {
+
+                public void run() {
+                    ValidationHelper.validateBean(context);
+                }
+            };
+        } else {
+            helper = new Runnable() {
+
+                public void run() {
+                    ValidationHelper.validateProperty(context);
+                }
+            };
+        }
+        Group currentGroup = context.getCurrentGroup();
+        List<Group> defaultGroups = expandDefaultGroup(context);
+        if (defaultGroups != null) {
+            for (Group each : defaultGroups) {
+                context.setCurrentGroup(each);
+                helper.run();
+                // continue validation, even if errors already found
+            }
+            context.setCurrentGroup(currentGroup); // restore
+        } else {
+            helper.run();
+        }
+    }
+
+    /**
+     * Create a {@link GroupValidationContext}.
+     *
+     * @param <T>
+     * @param metaBean
+     * @param object
+     * @param objectClass
+     * @param groups
+     * @return {@link GroupValidationContext} instance
+     */
+    protected <T> GroupValidationContext<T> createContext(MetaBean metaBean, T object, Class<T> objectClass,
+        Class<?>... groups) {
+        ConstraintValidationListener<T> listener = new ConstraintValidationListener<T>(object, objectClass);
+        GroupValidationContextImpl<T> context =
+            new GroupValidationContextImpl<T>(listener, this.factoryContext.getMessageInterpolator(),
+                this.factoryContext.getTraversableResolver(), metaBean);
+        context.setBean(object, metaBean);
+        context.setGroups(groupsComputer.computeGroups(groups));
+        return context;
+    }
+
+    /**
+     * Create a {@link BeanDescriptorImpl}
+     *
+     * @param metaBean
+     * @return {@link BeanDescriptorImpl} instance
+     */
+    protected BeanDescriptorImpl createBeanDescriptor(MetaBean metaBean) {
+        return new BeanDescriptorImpl(factoryContext, metaBean);
+    }
+
+    private boolean treatMapsLikeBeans = false;
+
+    /**
+     * Behavior configuration -
+     * <p/>
+     * <pre>
+     * @return treatMapsLikeBeans - true (validate maps like beans, so that
+     *                              you can use Maps to validate dynamic classes or
+     *                              beans for which you have the MetaBean but no instances)
+     *                            - false (default), validate maps like collections
+     *                              (validating the values only)
+     * </pre>
+     * <p/>
+     * (is still configuration to better in BeanValidationContext?)
+     */
+    public boolean isTreatMapsLikeBeans() {
+        return treatMapsLikeBeans;
+    }
+
+    /**
+     * Set whether maps are to be treated like beans.
+     * 
+     * <pre>
+     * @param treatMapsLikeBeans - true (validate maps like beans, so that
+     *                             you can use Maps to validate dynamic classes or
+     *                             beans for which you have the MetaBean but no instances)
+     *                           - false (default), validate maps like collections
+     *                             (validating the values only)
+     * </pre>
+     */
+    public void setTreatMapsLikeBeans(boolean treatMapsLikeBeans) {
+        this.treatMapsLikeBeans = treatMapsLikeBeans;
+    }
+
+    /**
+     * Checks that beanType is valid according to spec Section 4.1.1 i. Throws an {@link IllegalArgumentException} if it
+     * is not.
+     *
+     * @param beanType Bean type to check.
+     */
+    private <T> Class<T> checkBeanType(Class<T> beanType) {
+        if (beanType == null) {
+            throw new IllegalArgumentException("Bean type cannot be null.");
+        }
+        return beanType;
+    }
+
+    /**
+     * Checks that the property name is valid according to spec Section 4.1.1 i. Throws an
+     * {@link IllegalArgumentException} if it is not.
+     *
+     * @param propertyName Property name to check.
+     */
+    private void checkPropertyName(String propertyName) {
+        if (propertyName == null || propertyName.trim().length() == 0) {
+            throw new IllegalArgumentException("Property path cannot be null or empty.");
+        }
+    }
+
+    /**
+     * Checks that the groups array is valid according to spec Section 4.1.1 i. Throws an
+     * {@link IllegalArgumentException} if it is not.
+     *
+     * @param groups The groups to check.
+     */
+    private void checkGroups(Class<?>[] groups) {
+        if (groups == null) {
+            throw new IllegalArgumentException("Groups cannot be null.");
+        }
+    }
+
+    /**
+     * Dispatches a call from {@link #validate()} to {@link ClassValidator#validateBeanNet(GroupValidationContext)} with
+     * the current context set.
+     */
+    protected class Jsr303ValidationCallback implements ValidationHelper.ValidateCallback {
+
+        private final GroupValidationContext<?> context;
+
+        public Jsr303ValidationCallback(GroupValidationContext<?> context) {
+            this.context = context;
+        }
+
+        public void validate() {
+            validateBeanNet(context);
+        }
+
+    }
+
+    /**
+     * Create a {@link ValidationContextTraversal} instance for this {@link ClassValidator}.
+     * 
+     * @param validationContext
+     * @return {@link ValidationContextTraversal}
+     */
+    protected ValidationContextTraversal createValidationContextTraversal(GroupValidationContext<?> validationContext) {
+        return new ValidationContextTraversal(validationContext);
+    }
+
+    /**
+     * Implement {@link #validateProperty(Object, String, boolean, Class...)} and
+     * {@link #validateValue(Class, String, Object, boolean, Class...)}.
+     * 
+     * @param <T>
+     * @param beanType
+     * @param object
+     * @param propertyName
+     * @param value
+     * @param cascade
+     * @param groups
+     * @return {@link ConstraintViolation} {@link Set}
+     */
+    private <T> Set<ConstraintViolation<T>> validateValueImpl(Class<T> beanType, T object, String propertyName,
+        Object value, final boolean cascade, Class<?>... groups) {
+
+        assert (object == null) ^ (value == VALIDATE_PROPERTY);
+        checkPropertyName(propertyName);
+        checkGroups(groups);
+
+        try {
+            final MetaBean initialMetaBean = new DynamicMetaBean(getMetaBeanFinder());
+            initialMetaBean.setBeanClass(beanType);
+            GroupValidationContext<T> context = createContext(initialMetaBean, object, beanType, groups);
+            ValidationContextTraversal contextTraversal = createValidationContextTraversal(context);
+            PathNavigation.navigate(propertyName, contextTraversal);
+
+            MetaProperty prop = context.getMetaProperty();
+            boolean fixed = false;
+            if (value != VALIDATE_PROPERTY) {
+                assert !context.getPropertyPath().isRootPath();
+                if (prop == null && value != null) {
+                    context.setMetaBean(getMetaBeanFinder().findForClass(value.getClass()));
+                }
+                if (!cascade) {
+                    //TCK doesn't care what type a property is if there are no constraints to validate:
+                    FeaturesCapable meta = prop == null ? context.getMetaBean() : prop;
+                    if (ArrayUtils.isEmpty(meta.getValidations())) {
+                        return Collections.<ConstraintViolation<T>> emptySet();
+                    }
+                }
+                if (!TypeUtils.isAssignable(value == null ? null : value.getClass(), contextTraversal.getType())) {
+                    throw new IncompatiblePropertyValueException(String.format(
+                        "%3$s is not a valid value for property %2$s of type %1$s", beanType, propertyName, value));
+                }
+                if (prop == null) {
+                    context.setBean(value);
+                } else {
+                    context.setFixedValue(value);
+                    fixed = true;
+                }
+            }
+            boolean doCascade = cascade && (prop == null || prop.getMetaBean() != null);
+
+            Object bean = context.getBean();
+
+            ConstraintValidationListener<T> result = context.getListener();
+            Groups sequence = context.getGroups();
+
+            // 1. process groups
+
+            for (Group current : sequence.getGroups()) {
+                context.setCurrentGroup(current);
+
+                if (!doCascade || prop != null) {
+                    validatePropertyInGroup(context);
+                }
+                if (doCascade) {
+                    contextTraversal.moveDownIfNecessary();
+                    if (context.getMetaBean() instanceof DynamicMetaBean) {
+                        context.setMetaBean(context.getMetaBean().resolveMetaBean(
+                            ObjectUtils.defaultIfNull(context.getBean(), contextTraversal.getRawType())));
+                    }
+                    validateBeanNet(context);
+                    if (prop != null) {
+                        context.moveUp(bean, prop.getParentMetaBean());
+                        context.setMetaProperty(prop);
+                        if (fixed) {
+                            context.setFixedValue(value);
+                        }
+                    }
+                }
+            }
+
+            // 2. process sequences
+
+            int groupViolations = result.getConstraintViolations().size();
+
+            outer: for (List<Group> eachSeq : sequence.getSequences()) {
+                for (Group current : eachSeq) {
+                    context.setCurrentGroup(current);
+
+                    if (!doCascade || prop != null) {
+                        validatePropertyInGroup(context);
+                    }
+                    if (doCascade) {
+                        contextTraversal.moveDownIfNecessary();
+                        if (context.getMetaBean() instanceof DynamicMetaBean) {
+                            context.setMetaBean(context.getMetaBean().resolveMetaBean(
+                                ObjectUtils.defaultIfNull(context.getBean(), contextTraversal.getRawType())));
+                        }
+                        validateBeanNet(context);
+                        if (prop != null) {
+                            context.moveUp(bean, prop.getParentMetaBean());
+                            context.setMetaProperty(prop);
+                            if (fixed) {
+                                context.setFixedValue(value);
+                            }
+                        }
+                    }
+                    /**
+                     * if one of the group process in the sequence leads to one or more validation failure, the groups
+                     * following in the sequence must not be processed
+                     */
+                    if (result.getConstraintViolations().size() > groupViolations)
+                        break outer;
+                }
+            }
+            return result.getConstraintViolations();
+        } catch (RuntimeException ex) {
+            throw unrecoverableValidationError(ex, ObjectUtils.defaultIfNull(object, value));
+        }
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConfigurationImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConfigurationImpl.java
new file mode 100644
index 0000000..27a1c42
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConfigurationImpl.java
@@ -0,0 +1,340 @@
+/*
+ * 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.bval.jsr303;
+
+
+import org.apache.bval.jsr303.resolver.DefaultTraversableResolver;
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.jsr303.xml.ValidationParser;
+
+import javax.validation.*;
+import javax.validation.spi.BootstrapState;
+import javax.validation.spi.ConfigurationState;
+import javax.validation.spi.ValidationProvider;
+import java.io.InputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import java.util.logging.Logger;
+
+/**
+ * Description: used to configure apache-validation for jsr303.
+ * Implementation of Configuration that also implements ConfigurationState,
+ * hence this can be passed to buildValidatorFactory(ConfigurationState).
+ * <br/>
+ */
+public class ConfigurationImpl implements ApacheValidatorConfiguration, ConfigurationState {
+    private static final Logger log = Logger.getLogger(ConfigurationImpl.class.getName());
+
+    /**
+     * Configured {@link ValidationProvider}
+     */
+    //couldn't this be parameterized <ApacheValidatorConfiguration> or <? super ApacheValidatorConfiguration>?
+    protected final ValidationProvider<?> provider;
+
+    /**
+     * Configured {@link ValidationProviderResolver}
+     */
+    protected final ValidationProviderResolver providerResolver;
+
+    /**
+     * Configured {@link ValidationProvider} class
+     */
+    protected Class<? extends ValidationProvider<?>> providerClass;
+
+    /**
+     * Configured {@link MessageInterpolator}
+     */
+    protected MessageInterpolator messageInterpolator;
+
+    /**
+     * Configured {@link ConstraintValidatorFactory}
+     */
+    protected ConstraintValidatorFactory constraintValidatorFactory;
+
+    private TraversableResolver traversableResolver;
+
+    // BEGIN DEFAULTS
+    /**
+     * false = dirty flag (to prevent from multiple parsing validation.xml)
+     */
+    private boolean prepared = false;
+    private final TraversableResolver defaultTraversableResolver =
+          new DefaultTraversableResolver();
+
+    /**
+     * Default {@link MessageInterpolator}
+     */
+    protected final MessageInterpolator defaultMessageInterpolator =
+          new DefaultMessageInterpolator();
+
+    private final ConstraintValidatorFactory defaultConstraintValidatorFactory =
+          new DefaultConstraintValidatorFactory();
+    // END DEFAULTS
+
+    private Set<InputStream> mappingStreams = new HashSet<InputStream>();
+    private Map<String, String> properties = new HashMap<String, String>();
+    private boolean ignoreXmlConfiguration = false;
+
+    /**
+     * Create a new ConfigurationImpl instance.
+     * @param aState
+     * @param aProvider
+     */
+    public ConfigurationImpl(BootstrapState aState, ValidationProvider<?> aProvider) {
+        if (aProvider != null) {
+            this.provider = aProvider;
+            this.providerResolver = null;
+        } else if (aState != null) {
+            this.provider = null;
+            if (aState.getValidationProviderResolver() == null) {
+                providerResolver = aState.getDefaultValidationProviderResolver();
+            } else {
+                providerResolver = aState.getValidationProviderResolver();
+            }
+        } else {
+            throw new ValidationException("either provider or state are required");
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ApacheValidatorConfiguration traversableResolver(TraversableResolver resolver) {
+        traversableResolver = resolver;
+        this.prepared = false;
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     * Ignore data from the <i>META-INF/validation.xml</i> file if this
+     * method is called.
+     *
+     * @return this
+     */
+    public ApacheValidatorConfiguration ignoreXmlConfiguration() {
+        ignoreXmlConfiguration = true;
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConfigurationImpl messageInterpolator(MessageInterpolator resolver) {
+        this.messageInterpolator = resolver;
+        this.prepared = false;
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConfigurationImpl constraintValidatorFactory(
+          ConstraintValidatorFactory constraintFactory) {
+        this.constraintValidatorFactory = constraintFactory;
+        this.prepared = false;
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     * Add a stream describing constraint mapping in the Bean Validation
+     * XML format.
+     *
+     * @return this
+     */
+    public ApacheValidatorConfiguration addMapping(InputStream stream) {
+        mappingStreams.add(stream);
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     * Add a provider specific property. This property is equivalent to
+     * XML configuration properties.
+     * If we do not know how to handle the property, we silently ignore it.
+     *
+     * @return this
+     */
+    public ApacheValidatorConfiguration addProperty(String name, String value) {
+        properties.put(name, value);
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     * Return a map of non type-safe custom properties.
+     *
+     * @return null
+     */
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    /**
+     * {@inheritDoc}
+     * Returns true if Configuration.ignoreXMLConfiguration() has been called.
+     * In this case, we ignore META-INF/validation.xml
+     *
+     * @return true
+     */
+    public boolean isIgnoreXmlConfiguration() {
+        return ignoreXmlConfiguration;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<InputStream> getMappingStreams() {
+        return mappingStreams;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MessageInterpolator getMessageInterpolator() {
+        return messageInterpolator;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MessageInterpolator getDefaultMessageInterpolator() {
+        return defaultMessageInterpolator;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public TraversableResolver getDefaultTraversableResolver() {
+        return defaultTraversableResolver;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintValidatorFactory getDefaultConstraintValidatorFactory() {
+        return defaultConstraintValidatorFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     * main factory method to build a ValidatorFactory
+     *
+     * @throws ValidationException if the ValidatorFactory cannot be built
+     */
+    public ValidatorFactory buildValidatorFactory() {
+        return run(SecureActions.doPrivBuildValidatorFactory(this));
+    }
+
+    public ValidatorFactory doPrivBuildValidatorFactory() {
+        prepare();
+        if (provider != null) {
+            return provider.buildValidatorFactory(this);
+        } else {
+            return findProvider().buildValidatorFactory(this);
+        }
+    }
+
+    private void prepare() {
+        if (prepared) return;
+        parseValidationXml();
+        applyDefaults();
+        prepared = true;
+    }
+
+    /** Check whether a validation.xml file exists and parses it with JAXB */
+    private void parseValidationXml() {
+        if (isIgnoreXmlConfiguration()) {
+            log.info("ignoreXmlConfiguration == true");
+        } else {
+            new ValidationParser(getProperties().get(Properties.VALIDATION_XML_PATH))
+                  .processValidationConfig(this);
+        }
+    }
+
+    private void applyDefaults() {
+        // make sure we use the defaults in case they haven't been provided yet
+        if (traversableResolver == null) {
+            traversableResolver = getDefaultTraversableResolver();
+        }
+        if (messageInterpolator == null) {
+            messageInterpolator = getDefaultMessageInterpolator();
+        }
+        if (constraintValidatorFactory == null) {
+            constraintValidatorFactory = getDefaultConstraintValidatorFactory();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return the constraint validator factory of this configuration.
+     */
+    public ConstraintValidatorFactory getConstraintValidatorFactory() {
+        return constraintValidatorFactory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public TraversableResolver getTraversableResolver() {
+        return traversableResolver;
+    }
+
+    /**
+     * Get the configured {@link ValidationProvider}.
+     * @return {@link ValidationProvider}
+     */
+    public ValidationProvider<?> getProvider() {
+        return provider;
+    }
+
+    private ValidationProvider<?> findProvider() {
+        if (providerClass != null) {
+            for (ValidationProvider<?> provider : providerResolver
+                  .getValidationProviders()) {
+                if (providerClass.isAssignableFrom(provider.getClass())) {
+                    return provider;
+                }
+            }
+            throw new ValidationException(
+                  "Unable to find suitable provider: " + providerClass);
+        } else {
+            List<ValidationProvider<?>> providers = providerResolver.getValidationProviders();
+            return providers.get(0);
+        }
+    }
+
+    /**
+     * Set {@link ValidationProvider} class.
+     * @param providerClass
+     */
+    public void setProviderClass(Class<? extends ValidationProvider<?>> providerClass) {
+        this.providerClass = providerClass;
+    }
+
+    private static <T> T run(PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java
new file mode 100644
index 0000000..972e64f
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintAnnotationAttributes.java
@@ -0,0 +1,268 @@
+/**
+ *  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.bval.jsr303;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+/**
+ * Defines the well-known attributes of {@link Constraint} annotations.
+ * 
+ * @version $Rev: 1165923 $ $Date: 2011-09-06 18:07:53 -0500 (Tue, 06 Sep 2011) $
+ */
+public enum ConstraintAnnotationAttributes {
+    /**
+     * "message"
+     */
+    MESSAGE,
+
+    /**
+     * "groups"
+     */
+    GROUPS,
+
+    /**
+     * "payload"
+     */
+    PAYLOAD,
+
+    /**
+     * "value" for multi-valued constraints
+     */
+    VALUE(true);
+
+    @SuppressWarnings("unused")
+    private static class Types {
+        String message;
+        Class<?>[] groups;
+        Class<? extends Payload>[] payload;
+        Annotation[] value;
+    }
+
+    private Type type;
+    private boolean permitNullDefaultValue;
+
+    private ConstraintAnnotationAttributes() {
+        this(false);
+    }
+
+    private ConstraintAnnotationAttributes(boolean permitNullDefaultValue) {
+        this.permitNullDefaultValue = permitNullDefaultValue;
+        try {
+            this.type = Types.class.getDeclaredField(getAttributeName()).getGenericType();
+        } catch (Exception e) {
+            // should never happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Get the expected type of the represented attribute.
+     * 
+     * @return Class<?>
+     */
+    public Type getType() {
+        return type;
+    }
+
+    /**
+     * Get the attribute name represented.
+     * 
+     * @return String
+     */
+    public String getAttributeName() {
+        return name().toLowerCase(Locale.US);
+    }
+
+    /**
+     * Put <code>value</code> into a map with <code>this.attributeName</code> as
+     * key.
+     * 
+     * @param <V>
+     * @param map
+     * @param value
+     * @return previous value mapped to <code>this.attributeName</code>
+     */
+    public <V> Object put(Map<? super String, ? super V> map, V value) {
+        if (!TypeUtils.isInstance(value, getType())) {
+            throw new IllegalArgumentException(String.format("Invalid '%s' value: %s", getAttributeName(), value));
+        }
+        return map.put(getAttributeName(), value);
+    }
+
+    /**
+     * Get the value of <code>this.attributeName</code> from <code>map</code>.
+     * 
+     * @param <V>
+     * @param map
+     * @return V if you say so
+     */
+    public <V> V get(Map<? super String, ? super V> map) {
+        @SuppressWarnings("unchecked")
+        final V result = (V) map.get(getAttributeName());
+        if (!TypeUtils.isInstance(result, getType())) {
+            throw new IllegalStateException(String.format("Invalid '%s' value: %s", getAttributeName(), result));
+        }
+        return result;
+    }
+
+    /**
+     * Verify that this attribute is validly defined on the given type.
+     * 
+     * @param type
+     * @throws ConstraintDefinitionException
+     */
+    public <A extends Annotation> void validateOn(Class<A> type) {
+        new Worker<A>(type);
+    }
+
+    /**
+     * Benign means of checking for an attribute's existence.
+     * 
+     * @param type
+     * @return whether the attribute was (properly) declared
+     */
+    public <A extends Annotation> boolean isDeclaredOn(Class<A> type) {
+        return new Worker<A>(type, true).valid;
+    }
+
+    /**
+     * Get the value of this attribute from the specified constraint annotation.
+     * 
+     * @param constraint
+     * @return Object
+     */
+    public <T> T getValue(Annotation constraint) {
+        try {
+            @SuppressWarnings({ "rawtypes", "unchecked" })
+            T result = (T) new Worker(constraint.annotationType()).read(constraint);
+            return result;
+        } catch (Exception e) {
+            throw new ValidationException(String.format("Could not get value of %1$s() from %2$s", getType(),
+                constraint));
+        }
+    }
+
+    /**
+     * Get the default value of this attribute on the given annotation type.
+     * @param <T>
+     * @param type
+     * @return Object
+     */
+    public <T, A extends Annotation> T getDefaultValue(Class<A> type) {
+        @SuppressWarnings("unchecked")
+        final T result = (T) new Worker<A>(type).defaultValue;
+        return result;
+    }
+
+    private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+
+    private class Worker<C> {
+        final Method method;
+        final Object defaultValue;
+        final boolean valid;
+
+        /**
+         * Create a new Worker instance.
+         * @param constraintType to handle
+         */
+        Worker(Class<C> constraintType) {
+            this(constraintType, false);
+        }
+
+        /**
+         * Create a new Worker instance.
+         * @param constraintType to handle
+         * @param quiet whether to simply set !valid rather than throw an Exception on error
+         */
+        Worker(Class<C> constraintType, boolean quiet) {
+            super();
+            boolean _valid = true;
+            Object _defaultValue = null;
+            try {
+                method = doPrivileged(SecureActions.getPublicMethod(constraintType, getAttributeName()));
+                if (method == null) {
+                    if (quiet) {
+                        _valid = false;
+                        return;
+                    }
+                    throw new ConstraintDefinitionException(String.format("Annotation %1$s has no %2$s() method",
+                        constraintType, getAttributeName()));
+                }
+
+                if (!TypeUtils.isAssignable(method.getReturnType(), getType())) {
+                    if (quiet) {
+                        _valid = false;
+                        return;
+                    }
+                    throw new ConstraintDefinitionException(String.format(
+                        "Return type for %1$s() must be of type %2$s", getAttributeName(), getType()));
+                }
+                _defaultValue = method.getDefaultValue();
+                if (_defaultValue == null && permitNullDefaultValue) {
+                    return;
+                }
+                if (TypeUtils.isArrayType(getType()) && Array.getLength(_defaultValue) > 0) {
+                    if (quiet) {
+                        _valid = false;
+                        return;
+                    }
+                    throw new ConstraintDefinitionException(String.format(
+                        "Default value for %1$s() must be an empty array", getAttributeName()));
+                }
+            } finally {
+                valid = _valid;
+                defaultValue = _defaultValue;
+            }
+        }
+
+        <T> T read(final C constraint) {
+            @SuppressWarnings("unchecked")
+            T result = (T) doPrivileged(new PrivilegedAction<Object>() {
+                public Object run() {
+                    try {
+                        method.setAccessible(true);
+                        return method.invoke(constraint);
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            });
+            return result;
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintCached.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintCached.java
new file mode 100644
index 0000000..b144d3e
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintCached.java
@@ -0,0 +1,64 @@
+/*
+ * 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.bval.jsr303;
+
+import javax.validation.ConstraintValidator;
+import java.lang.annotation.Annotation;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Description: hold the relationship annotation->validatedBy[] ConstraintValidator classes
+ * that are already parsed in a cache.<br/>
+ */
+public class ConstraintCached {
+    private final Map<Class<? extends Annotation>, Class<? extends ConstraintValidator<?, ?>>[]> classes =
+          new HashMap<Class<? extends Annotation>, Class<? extends ConstraintValidator<?,?>>[]>();
+
+    /**
+     * Record the set of validator classes for a given constraint annotation.
+     * @param annotationClass
+     * @param definitionClasses
+     */
+    public <A extends Annotation> void putConstraintValidator(Class<A> annotationClass,
+                                                              Class<? extends ConstraintValidator<A, ?>>[] definitionClasses) {
+        classes.put(annotationClass, definitionClasses);
+    }
+
+    /**
+     * Learn whether we have cached the validator classes for the requested constraint annotation.
+     * @param annotationClass to look up
+     * @return boolean
+     */
+    public boolean containsConstraintValidator(Class<? extends Annotation> annotationClass) {
+        return classes.containsKey(annotationClass);
+    }
+
+    /**
+     * Get the cached validator classes for the requested constraint annotation.
+     * @param annotationClass to look up
+     * @return array of {@link ConstraintValidator} implementation types
+     */
+    @SuppressWarnings("unchecked")
+    public <A extends Annotation> Class<? extends ConstraintValidator<A, ?>>[] getConstraintValidators(
+          Class<A> annotationClass) {
+        return (Class<? extends ConstraintValidator<A, ?>>[]) classes.get(annotationClass);
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDefaults.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDefaults.java
new file mode 100644
index 0000000..3eb01f5
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDefaults.java
@@ -0,0 +1,131 @@
+/*
+ * 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.bval.jsr303;
+
+import javax.validation.ConstraintValidator;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Description: Provides access to the default constraints/validator implementation classes built into the framework.
+ * These are configured in DefaultConstraints.properties.<br/>
+ */
+public class ConstraintDefaults {
+    private static final Logger log = Logger.getLogger(ConstraintDefaults.class.getName());
+    private static final String DEFAULT_CONSTRAINTS =
+          "org/apache/bval/jsr303/DefaultConstraints.properties";
+    
+    /**
+     * The default constraint data stored herein.
+     */
+    protected Map<String, Class<? extends ConstraintValidator<?, ?>>[]> defaultConstraints;
+
+    /**
+     * Create a new ConstraintDefaults instance.
+     */
+    public ConstraintDefaults() {
+        defaultConstraints = loadDefaultConstraints(DEFAULT_CONSTRAINTS);
+    }
+
+    /**
+     * Get the default constraint data.
+     * @return String-keyed map
+     */
+    public Map<String, Class<? extends ConstraintValidator<?, ?>>[]> getDefaultConstraints() {
+        return defaultConstraints;
+    }
+
+    /**
+     * Get the default validator implementation types for the specified constraint annotation type. 
+     * @param annotationType
+     * @return array of {@link ConstraintValidator} implementation classes
+     */
+    @SuppressWarnings("unchecked")
+    public <A extends Annotation> Class<? extends ConstraintValidator<A, ?>>[] getValidatorClasses(
+          Class<A> annotationType) {
+        return (Class<? extends ConstraintValidator<A, ?>>[]) getDefaultConstraints().get(annotationType.getName());
+    }
+
+    @SuppressWarnings("unchecked")
+    private Map<String, Class<? extends ConstraintValidator<?, ?>>[]> loadDefaultConstraints(String resource) {
+        Properties constraintProperties = new Properties();
+        final ClassLoader classloader = getClassLoader();
+        InputStream stream = classloader.getResourceAsStream(resource);
+        if (stream != null) {
+            try {
+                constraintProperties.load(stream);
+            } catch (IOException e) {
+                log.log(Level.SEVERE, String.format("Cannot load %s", resource), e);
+            }
+        } else {
+            log.log(Level.WARNING, String.format("Cannot find %s", resource));
+        }
+
+        Map<String, Class<? extends ConstraintValidator<?, ?>>[]> loadedConstraints
+                = new HashMap<String, Class<? extends ConstraintValidator<?,?>>[]>();
+        for (Map.Entry<Object, Object> entry : constraintProperties.entrySet()) {
+
+            StringTokenizer tokens = new StringTokenizer((String) entry.getValue(), ", ");
+            LinkedList<Class<?>> classes = new LinkedList<Class<?>>();
+            while (tokens.hasMoreTokens()) {
+                final String eachClassName = tokens.nextToken();
+
+                Class<?> constraintValidatorClass =
+                      run(new PrivilegedAction<Class<?>>() {
+                          public Class<?> run() {
+                              try {
+                                  return Class.forName(eachClassName, true, classloader);
+                              } catch (ClassNotFoundException e) {
+                                  log.log(Level.SEVERE, String.format("Cannot find class %s", eachClassName), e);
+                                  return null;
+                              }
+                          }
+                      });
+
+                if (constraintValidatorClass != null) classes.add(constraintValidatorClass);
+
+            }
+            loadedConstraints
+                  .put((String) entry.getKey(),
+                        (Class<? extends ConstraintValidator<?, ?>>[]) classes.toArray(new Class[classes.size()]));
+
+        }
+        return loadedConstraints;
+    }
+
+    private ClassLoader getClassLoader() {
+        ClassLoader classloader = Thread.currentThread().getContextClassLoader();
+        if (classloader == null) classloader = getClass().getClassLoader();
+        return classloader;
+    }
+
+    private static <T> T run(PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDescriptorImpl.java
new file mode 100644
index 0000000..c3d4b86
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintDescriptorImpl.java
@@ -0,0 +1,132 @@
+/*
+ * 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.bval.jsr303;
+
+import javax.validation.Payload;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Description: immutable, serializable implementation of ConstraintDescriptor
+ * interface of JSR303<br>
+ * User: roman.stumm<br>
+ * Date: 22.04.2010<br>
+ * Time: 10:21:23<br>
+ */
+public class ConstraintDescriptorImpl<T extends Annotation> implements ConstraintDescriptor<T>, Serializable {
+    /** Serialization version */
+    private static final long serialVersionUID = 1L;
+
+    private final T annotation;
+    private final Set<Class<?>> groups;
+    private final Set<Class<? extends javax.validation.Payload>> payload;
+    private final List<java.lang.Class<? extends javax.validation.ConstraintValidator<T, ?>>> constraintValidatorClasses;
+    private final Map<String, Object> attributes;
+    private final Set<ConstraintDescriptor<?>> composingConstraints;
+    private final boolean reportAsSingleViolation;
+
+    /**
+     * Create a new ConstraintDescriptorImpl instance.
+     * 
+     * @param descriptor
+     */
+    public ConstraintDescriptorImpl(ConstraintDescriptor<T> descriptor) {
+        this(descriptor.getAnnotation(), descriptor.getGroups(), descriptor.getPayload(), descriptor
+            .getConstraintValidatorClasses(), descriptor.getAttributes(), descriptor.getComposingConstraints(),
+            descriptor.isReportAsSingleViolation());
+    }
+
+    /**
+     * Create a new ConstraintDescriptorImpl instance.
+     * 
+     * @param annotation
+     * @param groups
+     * @param payload
+     * @param constraintValidatorClasses
+     * @param attributes
+     * @param composingConstraints
+     * @param reportAsSingleViolation
+     */
+    public ConstraintDescriptorImpl(T annotation, Set<Class<?>> groups,
+        Set<Class<? extends javax.validation.Payload>> payload,
+        List<java.lang.Class<? extends javax.validation.ConstraintValidator<T, ?>>> constraintValidatorClasses,
+        Map<String, Object> attributes, Set<ConstraintDescriptor<?>> composingConstraints,
+        boolean reportAsSingleViolation) {
+        this.annotation = annotation;
+        this.groups = groups;
+        this.payload = payload;
+        this.constraintValidatorClasses = constraintValidatorClasses;
+        this.attributes = attributes;
+        this.composingConstraints = composingConstraints;
+        this.reportAsSingleViolation = reportAsSingleViolation;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T getAnnotation() {
+        return annotation;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<Class<?>> getGroups() {
+        return groups;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<Class<? extends Payload>> getPayload() {
+        return payload;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<java.lang.Class<? extends javax.validation.ConstraintValidator<T, ?>>> getConstraintValidatorClasses() {
+        return constraintValidatorClasses;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<ConstraintDescriptor<?>> getComposingConstraints() {
+        return composingConstraints;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isReportAsSingleViolation() {
+        return reportAsSingleViolation;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintFinderImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintFinderImpl.java
new file mode 100644
index 0000000..fb8cd1a
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintFinderImpl.java
@@ -0,0 +1,162 @@
+/*
+ * 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.bval.jsr303;
+
+import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.groups.Groups;
+import org.apache.bval.jsr303.groups.GroupsComputer;
+import org.apache.bval.model.MetaBean;
+
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+import javax.validation.metadata.ElementDescriptor.ConstraintFinder;
+import javax.validation.metadata.Scope;
+import java.lang.annotation.ElementType;
+import java.util.*;
+
+/**
+ * Description: Implementation of the fluent {@link ConstraintFinder} interface.<br/>
+ */
+final class ConstraintFinderImpl implements ElementDescriptor.ConstraintFinder {
+    private final MetaBean metaBean;
+    private final Set<Scope> findInScopes;
+    private Set<ConstraintValidation<?>> constraintDescriptors;
+
+    /**
+     * Create a new ConstraintFinderImpl instance.
+     * 
+     * @param metaBean
+     * @param constraintDescriptors
+     */
+    ConstraintFinderImpl(MetaBean metaBean, Set<ConstraintValidation<?>> constraintDescriptors) {
+        this.metaBean = metaBean;
+        this.constraintDescriptors = constraintDescriptors;
+        this.findInScopes = new HashSet<Scope>(Arrays.asList(Scope.values()));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ElementDescriptor.ConstraintFinder unorderedAndMatchingGroups(Class<?>... groups) {
+        Set<ConstraintValidation<?>> matchingDescriptors =
+            new HashSet<ConstraintValidation<?>>(constraintDescriptors.size());
+        Groups groupChain = new GroupsComputer().computeGroups(groups);
+        for (Group group : groupChain.getGroups()) {
+            if (group.isDefault()) {
+                // If group is default, check if it gets redefined
+                List<Group> expandedDefaultGroup = metaBean.getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
+                for (Group defaultGroupMember : expandedDefaultGroup) {
+                    for (ConstraintValidation<?> descriptor : constraintDescriptors) {
+                        if (isInScope(descriptor) && isInGroup(descriptor, defaultGroupMember)) {
+                            matchingDescriptors.add(descriptor);
+                        }
+                    }
+                }
+            } else {
+                for (ConstraintValidation<?> descriptor : constraintDescriptors) {
+                    if (isInScope(descriptor) && isInGroup(descriptor, group)) {
+                        matchingDescriptors.add(descriptor);
+                    }
+                }
+            }
+        }
+        return thisWith(matchingDescriptors);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ElementDescriptor.ConstraintFinder lookingAt(Scope scope) {
+        if (scope.equals(Scope.LOCAL_ELEMENT)) {
+            findInScopes.remove(Scope.HIERARCHY);
+            for (Iterator<ConstraintValidation<?>> it = constraintDescriptors.iterator(); it.hasNext();) {
+                ConstraintValidation<?> cv = it.next();
+                if (cv.getOwner() != metaBean.getBeanClass()) {
+                    it.remove();
+                }
+            }
+        }
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ElementDescriptor.ConstraintFinder declaredOn(ElementType... elementTypes) {
+        Set<ConstraintValidation<?>> matchingDescriptors =
+            new HashSet<ConstraintValidation<?>>(constraintDescriptors.size());
+        for (ElementType each : elementTypes) {
+            for (ConstraintValidation<?> descriptor : constraintDescriptors) {
+                if (isInScope(descriptor) && isAtElement(descriptor, each)) {
+                    matchingDescriptors.add(descriptor);
+                }
+            }
+        }
+        return thisWith(matchingDescriptors);
+    }
+
+    private boolean isAtElement(ConstraintValidation<?> descriptor, ElementType each) {
+        return descriptor.getAccess().getElementType() == each;
+    }
+
+    private boolean isInScope(ConstraintValidation<?> descriptor) {
+        if (findInScopes.size() == Scope.values().length)
+            return true; // all scopes
+        if (metaBean != null) {
+            Class<?> owner = descriptor.getOwner();
+            for (Scope scope : findInScopes) {
+                switch (scope) {
+                case LOCAL_ELEMENT:
+                    if (owner.equals(metaBean.getBeanClass()))
+                        return true;
+                    break;
+                case HIERARCHY:
+                    if (!owner.equals(metaBean.getBeanClass()))
+                        return true;
+                    break;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean isInGroup(ConstraintValidation<?> descriptor, Group group) {
+        return descriptor.getGroups().contains(group.getGroup());
+    }
+
+    private ElementDescriptor.ConstraintFinder thisWith(Set<ConstraintValidation<?>> matchingDescriptors) {
+        constraintDescriptors = matchingDescriptors;
+        return this;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<ConstraintDescriptor<?>> getConstraintDescriptors() {
+        return constraintDescriptors.isEmpty() ? Collections.<ConstraintDescriptor<?>> emptySet() : Collections
+            .<ConstraintDescriptor<?>> unmodifiableSet(constraintDescriptors);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean hasConstraints() {
+        return !constraintDescriptors.isEmpty();
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java
new file mode 100644
index 0000000..cd9ec70
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidation.java
@@ -0,0 +1,381 @@
+/*
+ * 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.bval.jsr303;
+
+import org.apache.bval.jsr303.util.NodeImpl;
+import org.apache.bval.jsr303.util.PathImpl;
+import org.apache.bval.model.Validation;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.commons.lang3.ArrayUtils;
+
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.ConstraintValidator;
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.util.*;
+
+/**
+ * Description: Adapter between Constraint (JSR303) and Validation (Core)<br/>
+ * this instance is immutable!<br/>
+ */
+public class ConstraintValidation<T extends Annotation> implements Validation, ConstraintDescriptor<T> {
+    private final ConstraintValidator<T, ?> validator;
+    private T annotation; // for metadata request API
+    private final AccessStrategy access;
+    private final boolean reportFromComposite;
+    private final Map<String, Object> attributes;
+
+    private Set<ConstraintValidation<?>> composedConstraints;
+
+    /**
+     * the owner is the type where the validation comes from. it is used to
+     * support implicit grouping.
+     */
+    private final Class<?> owner;
+    private Set<Class<?>> groups;
+    private Set<Class<? extends Payload>> payload;
+    private Class<? extends ConstraintValidator<T, ?>>[] validatorClasses;
+
+    /**
+     * Create a new ConstraintValidation instance.
+     * 
+     * @param validatorClasses
+     * @param validator
+     *            - the constraint validator
+     * @param annotation
+     *            - the annotation of the constraint
+     * @param owner
+     *            - the type where the annotated element is placed (class,
+     *            interface, annotation type)
+     * @param access
+     *            - how to access the value
+     * @param reportFromComposite
+     */
+    public ConstraintValidation(Class<? extends ConstraintValidator<T, ?>>[] validatorClasses,
+        ConstraintValidator<T, ?> validator, T annotation, Class<?> owner, AccessStrategy access,
+        boolean reportFromComposite) {
+        this.attributes = new HashMap<String, Object>();
+        this.validatorClasses = ArrayUtils.clone(validatorClasses);
+        this.validator = validator;
+        this.annotation = annotation;
+        this.owner = owner;
+        this.access = access;
+        this.reportFromComposite = reportFromComposite;
+    }
+
+    /**
+     * Return a {@link Serializable} {@link ConstraintDescriptor} capturing a
+     * snapshot of current state.
+     * 
+     * @return {@link ConstraintDescriptor}
+     */
+    public ConstraintDescriptor<T> asSerializableDescriptor() {
+        return new ConstraintDescriptorImpl<T>(this);
+    }
+
+    /**
+     * Set the applicable validation groups.
+     * 
+     * @param groups
+     */
+    void setGroups(Set<Class<?>> groups) {
+        this.groups = groups;
+        ConstraintAnnotationAttributes.GROUPS.put(attributes, groups.toArray(new Class[groups.size()]));
+    }
+
+    /**
+     * Set the payload.
+     * 
+     * @param payload
+     */
+    void setPayload(Set<Class<? extends Payload>> payload) {
+        this.payload = payload;
+        ConstraintAnnotationAttributes.PAYLOAD.put(attributes, payload.toArray(new Class[payload.size()]));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isReportAsSingleViolation() {
+        return reportFromComposite;
+    }
+
+    /**
+     * Add a composing constraint.
+     * 
+     * @param aConstraintValidation
+     *            to add
+     */
+    public void addComposed(ConstraintValidation<?> aConstraintValidation) {
+        if (composedConstraints == null) {
+            composedConstraints = new HashSet<ConstraintValidation<?>>();
+        }
+        composedConstraints.add(aConstraintValidation);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <L extends ValidationListener> void validate(ValidationContext<L> context) {
+        validate((GroupValidationContext<?>) context);
+    }
+
+    /**
+     * Validate a {@link GroupValidationContext}.
+     * 
+     * @param context
+     *            root
+     */
+    public void validate(GroupValidationContext<?> context) {
+        context.setConstraintValidation(this);
+        /**
+         * execute unless the given validation constraint has already been
+         * processed during this validation routine (as part of a previous group
+         * match)
+         */
+        if (!isMemberOf(context.getCurrentGroup().getGroup())) {
+            return; // do not validate in the current group
+        }
+        if (context.getCurrentOwner() != null && this.owner != context.getCurrentOwner()) {
+            return;
+        }
+        if (validator != null && !context.collectValidated(validator))
+            return; // already done
+
+        if (context.getMetaProperty() != null && !isReachable(context)) {
+            return;
+        }
+
+        // process composed constraints
+        if (isReportAsSingleViolation()) {
+            ConstraintValidationListener<?> listener = context.getListener();
+            listener.beginReportAsSingle();
+
+            boolean failed = listener.hasViolations();
+            try {
+                // stop validating when already failed and
+                // ReportAsSingleInvalidConstraint = true ?
+                for (Iterator<ConstraintValidation<?>> composed = getComposingValidations().iterator(); !failed && composed.hasNext();) {
+                    composed.next().validate(context);
+                    failed = listener.hasViolations();
+                }
+            } finally {
+                listener.endReportAsSingle();
+                // Restore current constraint validation
+                context.setConstraintValidation(this);
+            }
+
+            if (failed) {
+                // TODO RSt - how should the composed constraint error report look like?
+                ConstraintValidatorContextImpl jsrContext = new ConstraintValidatorContextImpl(context, this);
+                addErrors(context, jsrContext); // add defaultErrorMessage only
+                return;
+            }
+        } else {
+            for (ConstraintValidation<?> composed : getComposingValidations()) {
+                composed.validate(context);
+            }
+
+            // Restore current constraint validation
+            context.setConstraintValidation(this);
+        }
+
+        if (validator != null) {
+            ConstraintValidatorContextImpl jsrContext = new ConstraintValidatorContextImpl(context, this);
+            @SuppressWarnings("unchecked")
+            final ConstraintValidator<T, Object> objectValidator = (ConstraintValidator<T, Object>) validator;
+            if (!objectValidator.isValid(context.getValidatedValue(), jsrContext)) {
+                addErrors(context, jsrContext);
+            }
+        }
+    }
+
+    /**
+     * Initialize the validator (if not <code>null</code>) with the stored
+     * annotation.
+     */
+    public void initialize() {
+        if (null != validator) {
+            try {
+                validator.initialize(annotation);
+            } catch (RuntimeException e) {
+                // Either a "legit" problem initializing the validator or a
+                // ClassCastException if the validator associated annotation is
+                // not a supertype of the validated annotation.
+                throw new ConstraintDefinitionException("Incorrect validator ["
+                    + validator.getClass().getCanonicalName() + "] for annotation "
+                    + annotation.annotationType().getCanonicalName(), e);
+            }
+        }
+    }
+
+    private boolean isReachable(GroupValidationContext<?> context) {
+        PathImpl path = context.getPropertyPath();
+        NodeImpl node = path.getLeafNode();
+        PathImpl beanPath = path.getPathWithoutLeafNode();
+        if (beanPath == null) {
+            beanPath = PathImpl.create(null);
+        }
+        try {
+            if (!context.getTraversableResolver().isReachable(context.getBean(), node,
+                context.getRootMetaBean().getBeanClass(), beanPath, access.getElementType()))
+                return false;
+        } catch (RuntimeException e) {
+            throw new ValidationException("Error in TraversableResolver.isReachable() for " + context.getBean(), e);
+        }
+
+        return true;
+    }
+
+    private void addErrors(GroupValidationContext<?> context, ConstraintValidatorContextImpl jsrContext) {
+        for (ValidationListener.Error each : jsrContext.getErrorMessages()) {
+            context.getListener().addError(each, context);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return "ConstraintValidation{" + validator + '}';
+    }
+
+    /**
+     * Get the message template used by this constraint.
+     * 
+     * @return String
+     */
+    public String getMessageTemplate() {
+        return ConstraintAnnotationAttributes.MESSAGE.get(attributes);
+    }
+
+    /**
+     * Get the {@link ConstraintValidator} invoked by this
+     * {@link ConstraintValidation}.
+     * 
+     * @return
+     */
+    public ConstraintValidator<T, ?> getValidator() {
+        return validator;
+    }
+
+    /**
+     * Learn whether this {@link ConstraintValidation} belongs to the specified
+     * group.
+     * 
+     * @param reqGroup
+     * @return boolean
+     */
+    protected boolean isMemberOf(Class<?> reqGroup) {
+        return groups.contains(reqGroup);
+    }
+
+    /**
+     * Get the owning class of this {@link ConstraintValidation}.
+     * 
+     * @return Class
+     */
+    public Class<?> getOwner() {
+        return owner;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public T getAnnotation() {
+        return annotation;
+    }
+
+    /**
+     * Get the {@link AccessStrategy} used by this {@link ConstraintValidation}.
+     * 
+     * @return {@link AccessStrategy}
+     */
+    public AccessStrategy getAccess() {
+        return access;
+    }
+
+    /**
+     * Override the Annotation set at construction.
+     * 
+     * @param annotation
+     */
+    public void setAnnotation(T annotation) {
+        this.annotation = annotation;
+    }
+
+    // ///////////////////////// ConstraintDescriptor implementation
+
+    /**
+     * {@inheritDoc}
+     */
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @SuppressWarnings("unchecked")
+    public Set<ConstraintDescriptor<?>> getComposingConstraints() {
+        return composedConstraints == null ? Collections.EMPTY_SET : composedConstraints;
+    }
+
+    /**
+     * Get the composing {@link ConstraintValidation} objects. This is
+     * effectively an implementation-specific analogue to
+     * {@link #getComposingConstraints()}.
+     * 
+     * @return {@link Set} of {@link ConstraintValidation}
+     */
+    @SuppressWarnings("unchecked")
+    Set<ConstraintValidation<?>> getComposingValidations() {
+        return composedConstraints == null ? Collections.EMPTY_SET : composedConstraints;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<Class<?>> getGroups() {
+        return groups;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<Class<? extends Payload>> getPayload() {
+        return payload;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<Class<? extends ConstraintValidator<T, ?>>> getConstraintValidatorClasses() {
+        if (validatorClasses == null) {
+            return Collections.emptyList();
+        }
+        return Arrays.asList(validatorClasses);
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java
new file mode 100644
index 0000000..cf0972b
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidationListener.java
@@ -0,0 +1,175 @@
+/*
+ * 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.bval.jsr303;
+
+
+import org.apache.bval.jsr303.util.PathImpl;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.MessageInterpolator;
+import javax.validation.Path;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.lang.annotation.ElementType;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Description: JSR-303 {@link ValidationListener} implementation; provides {@link ConstraintViolation}s.<br/>
+ * 
+ * @version $Rev$ $Date$
+ */
+public final class ConstraintValidationListener<T> implements ValidationListener {
+    private final Set<ConstraintViolation<T>> constraintViolations = new HashSet<ConstraintViolation<T>>();
+    private final T rootBean;
+    private final Class<T> rootBeanType;
+    // the validation process is single-threaded and it's unlikely to change in the near future (otherwise use AtomicInteger).
+    private int compositeDepth = 0;
+    private boolean hasCompositeError;
+
+    /**
+     * Create a new ConstraintValidationListener instance.
+     * @param aRootBean
+     * @param rootBeanType
+     */
+    public ConstraintValidationListener(T aRootBean, Class<T> rootBeanType) {
+        this.rootBean = aRootBean;
+        this.rootBeanType = rootBeanType;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <VL extends ValidationListener> void addError(String reason, ValidationContext<VL> context) {
+        addError(reason, null, context);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <VL extends ValidationListener> void addError(Error error, ValidationContext<VL> context) {
+        if (error.getOwner() instanceof Path) {
+            addError(error.getReason(), (Path) error.getOwner(), context);
+        } else {
+            addError(error.getReason(), null, context);
+        }
+    }
+
+    private void addError(String messageTemplate, Path propPath,
+                          ValidationContext<?> context) {
+        if (compositeDepth > 0) {
+            hasCompositeError |= true;
+            return;
+        }
+        final Object value;
+
+        final ConstraintDescriptor<?> descriptor;
+        final String message;
+        if (context instanceof GroupValidationContext<?>) {
+            GroupValidationContext<?> gcontext = (GroupValidationContext<?>) context;
+            value = gcontext.getValidatedValue();
+            if (gcontext instanceof MessageInterpolator.Context) {
+                message = gcontext.getMessageResolver()
+                      .interpolate(messageTemplate,
+                            (MessageInterpolator.Context) gcontext);
+            } else {
+                message =
+                      gcontext.getMessageResolver().interpolate(messageTemplate, null);
+            }
+            descriptor = gcontext.getConstraintValidation().asSerializableDescriptor();
+            if (propPath == null) propPath = gcontext.getPropertyPath();
+        } else {
+            if (context.getMetaProperty() == null) value = context.getBean();
+            else value = context.getPropertyValue();
+            message = messageTemplate;
+            if (propPath == null)
+                propPath = PathImpl.createPathFromString(context.getPropertyName());
+            descriptor = null;
+        }
+        ElementType elementType = (context.getAccess() != null) ? context.getAccess().getElementType() : null;
+        ConstraintViolationImpl<T> ic = new ConstraintViolationImpl<T>(messageTemplate,
+              message, rootBean, context.getBean(), propPath, value, descriptor, rootBeanType, elementType);
+        constraintViolations.add(ic);
+    }
+
+    /**
+     * Get the {@link ConstraintViolation}s accumulated by this {@link ConstraintValidationListener}.
+     * @return {@link Set} of {@link ConstraintViolation}
+     */
+    public Set<ConstraintViolation<T>> getConstraintViolations() {
+        return constraintViolations;
+    }
+
+    /**
+     * Learn whether no violations were found. 
+     * @return boolean
+     */
+    public boolean isEmpty() {
+        return constraintViolations.isEmpty();
+    }
+
+    /**
+     * Get the root bean.
+     * @return T
+     */
+    public T getRootBean() {
+        return rootBean;
+    }
+
+    /**
+     * Get the root bean type of this {@link ConstraintValidationListener}.
+     * @return Class<T>
+     */
+    public Class<T> getRootBeanType() {
+        return rootBeanType;
+    }
+    
+    /**
+     * Get the count of encountered violations.
+     * @return int
+     */
+    public int violationsSize() {
+        return constraintViolations.size();
+    }
+
+    /**
+     * Learn whether there are violations available.
+     * If in report-as-single-violation mode, the result is scoped accordingly.
+     * Note that this means you must check before exiting report-as-single-violation mode
+     * @return boolean
+     */
+    public boolean hasViolations() {
+        return compositeDepth == 0 ? !constraintViolations.isEmpty() : hasCompositeError;
+    }
+
+    /**
+     * Signify the beginning of a report-as-single-violation composite validation.
+     * @return <code>true</code> as this call caused the listener to enter report-as-single-violation mode
+     */
+    public boolean beginReportAsSingle() {
+        return ++compositeDepth == 1;
+    }
+
+    /**
+     * Signify the end of a report-as-single-violation composite validation.
+     * @return <code>true</code> as this call caused the listener to exit report-as-single-violation mode
+     */
+    public boolean endReportAsSingle() {
+        return --compositeDepth == 0;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorContextImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorContextImpl.java
new file mode 100644
index 0000000..cad7941
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorContextImpl.java
@@ -0,0 +1,160 @@
+/*
+ * 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.bval.jsr303;
+
+
+import org.apache.bval.jsr303.util.NodeBuilderDefinedContextImpl;
+import org.apache.bval.jsr303.util.NodeImpl;
+import org.apache.bval.jsr303.util.PathImpl;
+import org.apache.bval.model.ValidationListener;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.Path;
+import javax.validation.ValidationException;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Description: Short-lived {@link ConstraintValidatorContext} implementation passed by
+ * a {@link ConstraintValidation} to its adapted {@link ConstraintValidator}. <br/>
+ */
+public class ConstraintValidatorContextImpl implements ConstraintValidatorContext {
+    private final List<ValidationListener.Error> errorMessages =
+          new LinkedList<ValidationListener.Error>();
+
+    private final ConstraintValidation<?> constraintDescriptor;
+    private final GroupValidationContext<?> validationContext;
+
+    private boolean defaultDisabled;
+
+    /**
+     * Create a new ConstraintValidatorContextImpl instance.
+     * @param validationContext
+     * @param aConstraintValidation
+     */
+    public ConstraintValidatorContextImpl(GroupValidationContext<?> validationContext,
+                                          ConstraintValidation<?> aConstraintValidation) {
+        this.validationContext = validationContext;
+        this.constraintDescriptor = aConstraintValidation;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void disableDefaultConstraintViolation() {
+        defaultDisabled = true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getDefaultConstraintMessageTemplate() {
+        return constraintDescriptor.getMessageTemplate();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintViolationBuilder buildConstraintViolationWithTemplate(
+          String messageTemplate) {
+        return new ConstraintViolationBuilderImpl(this, messageTemplate,
+              validationContext.getPropertyPath());
+    }
+
+    private static final class ConstraintViolationBuilderImpl
+          implements ConstraintValidatorContext.ConstraintViolationBuilder {
+        private final ConstraintValidatorContextImpl parent;
+        private final String messageTemplate;
+        private final PathImpl propertyPath;
+
+        /**
+         * Create a new ConstraintViolationBuilderImpl instance.
+         * @param contextImpl
+         * @param template
+         * @param path
+         */
+        ConstraintViolationBuilderImpl(ConstraintValidatorContextImpl contextImpl,
+                                       String template, PathImpl path) {
+            parent = contextImpl;
+            messageTemplate = template;
+            propertyPath = path;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public NodeBuilderDefinedContext addNode(String name) {
+            PathImpl path;
+            if (propertyPath.isRootPath()) {
+                path = PathImpl.create(name);
+            } else {
+                path = PathImpl.copy(propertyPath);
+                path.addNode(new NodeImpl(name));
+            }
+            return new NodeBuilderDefinedContextImpl(parent, messageTemplate, path);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public ConstraintValidatorContext addConstraintViolation() {
+            parent.addError(messageTemplate, propertyPath);
+            return parent;
+        }
+    }
+
+    /**
+     * Get the queued error messages.
+     * @return List
+     */
+    public List<ValidationListener.Error> getErrorMessages() {
+        if (defaultDisabled && errorMessages.isEmpty()) {
+            throw new ValidationException(
+                  "At least one custom message must be created if the default error message gets disabled.");
+        }
+
+        List<ValidationListener.Error> returnedErrorMessages =
+              new ArrayList<ValidationListener.Error>(errorMessages);
+        if (!defaultDisabled) {
+            returnedErrorMessages.add(new ValidationListener.Error(
+                  getDefaultConstraintMessageTemplate(), validationContext.getPropertyPath(),
+                  null));
+        }
+        return returnedErrorMessages;
+    }
+
+    /**
+     * Get this {@link ConstraintValidatorContext}'s {@link GroupValidationContext}.
+     * @return {@link GroupValidationContext}
+     */
+    public GroupValidationContext<?> getValidationContext() {
+        return validationContext;
+    }
+
+    /**
+     * Add an error message to this {@link ConstraintValidatorContext}.
+     * @param messageTemplate
+     * @param propertyPath
+     */
+    public void addError(String messageTemplate, Path propertyPath) {
+        errorMessages.add(new ValidationListener.Error(messageTemplate, propertyPath, null));
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorIdentity.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorIdentity.java
new file mode 100644
index 0000000..7e4b6fa
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintValidatorIdentity.java
@@ -0,0 +1,134 @@
+/*
+ *  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.bval.jsr303;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.Path;
+
+/**
+ * Class that stores the needed properties to ensure that a validation is not
+ * checked more than once.
+ * <p>
+ * These properties are:
+ * <ul>
+ * <li>The ref of the bean to which the validation would be applied.</li>
+ * <li>The path of the property.</li>
+ * <li>The ref of the {@link ConstraintValidator}.</li>
+ * </ul>
+ * 
+ * @author Carlos Vara
+ */
+final class ConstraintValidatorIdentity {
+
+    private final Object bean;
+    private final Path path;
+    private final ConstraintValidator<?, ?> constraintValidator;
+
+    /**
+     * Create a new ConstraintValidatorIdentity instance.
+     * @param bean
+     * @param path
+     * @param constraintValidator
+     */
+    public ConstraintValidatorIdentity(Object bean, Path path, ConstraintValidator<?, ?> constraintValidator) {
+        this.bean = bean;
+        this.path = path;
+        this.constraintValidator = constraintValidator;
+    }
+
+    /**
+     * Get the referenced bean.
+     * @return Object
+     */
+    public Object getBean() {
+        return bean;
+    }
+
+    /**
+     * Get the referenced property {@link Path}.
+     * @return Path
+     */
+    public Path getPath() {
+        return path;
+    }
+
+    /**
+     * Get the associated {@link ConstraintValidator}.
+     * @return {@link ConstraintValidator}
+     */
+    public ConstraintValidator<?, ?> getConstraintValidator() {
+        return constraintValidator;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object obj) {
+
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null) {
+            return false;
+        }
+
+        if (!(obj instanceof ConstraintValidatorIdentity)) {
+            return false;
+        }
+
+        ConstraintValidatorIdentity other = (ConstraintValidatorIdentity) obj;
+
+        // Bean ref must be the same
+        if (this.bean != other.bean) {
+            return false;
+        }
+
+        // ConstraintValidator ref must be the same
+        if (this.constraintValidator != other.constraintValidator) {
+            return false;
+        }
+
+        // Path must be equals
+        if (!this.path.equals(other.path)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((this.bean == null) ? 0 : this.bean.hashCode());
+        result = prime * result
+                + ((this.path == null) ? 0 : this.path.hashCode());
+        result = prime * result
+                + ((this.constraintValidator == null) ? 0 : this.constraintValidator.hashCode());
+        return result;
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintViolationImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintViolationImpl.java
new file mode 100644
index 0000000..1ced366
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ConstraintViolationImpl.java
@@ -0,0 +1,241 @@
+/*
+ * 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.bval.jsr303;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Path;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.io.Serializable;
+import java.lang.annotation.ElementType;
+
+/**
+ * Description: Describe a constraint validation defect.<br/>
+ * From rootBean and propertyPath, it is possible to rebuild the context of the failure
+ */
+class ConstraintViolationImpl<T> implements ConstraintViolation<T>, Serializable {
+    /** Serialization version */
+    private static final long serialVersionUID = 1L;
+
+    private final String messageTemplate;
+    private final String message;
+    /** root bean validation was invoked on. */
+    private final T rootBean;
+    private final Class<T> rootBeanClass;
+    /** last bean validated. */
+    private final Object leafBean;
+    private final Object value;
+    private final Path propertyPath;
+    private final ElementType elementType;
+    private final ConstraintDescriptor<?> constraintDescriptor;
+    
+    /**
+     * Create a new ConstraintViolationImpl instance.
+     * @param messageTemplate - message reason (raw message) 
+     * @param message - interpolated message (locale specific)
+     * @param rootBean
+     * @param leafBean
+     * @param propertyPath
+     * @param value
+     * @param constraintDescriptor
+     * @param rootBeanClass
+     * @param elementType
+     */
+    public ConstraintViolationImpl(String messageTemplate, String message, T rootBean, Object leafBean,
+                                   Path propertyPath, Object value,
+                                   ConstraintDescriptor<?> constraintDescriptor, Class<T> rootBeanClass, ElementType elementType) {
+        this.messageTemplate = messageTemplate;
+        this.message = message;
+        this.rootBean = rootBean;
+        this.rootBeanClass = rootBeanClass;
+        this.propertyPath = propertyPath;
+        this.leafBean = leafBean;
+        this.value = value;
+        this.constraintDescriptor = constraintDescriptor;
+        this.elementType = elementType;
+    }
+
+    /**
+     * {@inheritDoc}
+     * former name getInterpolatedMessage()
+     * @return The interpolated error message for this constraint violation.
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getMessageTemplate() {
+        return messageTemplate;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return Root bean being validated
+     */
+    public T getRootBean() {
+        return rootBean;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Class<T> getRootBeanClass() {
+        return rootBeanClass;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object getLeafBean() {
+        return leafBean;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return The value failing to pass the constraint
+     */
+    public Object getInvalidValue() {
+        return value;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return the property path to the value from <code>rootBean</code>
+     *         Null if the value is the rootBean itself
+     */
+    public Path getPropertyPath() {
+        return propertyPath;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintDescriptor<?> getConstraintDescriptor() {
+        return constraintDescriptor;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return "ConstraintViolationImpl{" + "rootBean=" + rootBean + ", propertyPath='" +
+              propertyPath + '\'' + ", message='" + message + '\'' + ", leafBean=" +
+              leafBean + ", value=" + value + '}';
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((this.leafBean == null) ? 0 : this.leafBean.hashCode());
+        result = prime * result
+                + ((this.message == null) ? 0 : this.message.hashCode());
+        result = prime
+                * result
+                + ((this.propertyPath == null) ? 0 : this.propertyPath
+                        .hashCode());
+        result = prime * result
+                + ((this.rootBean == null) ? 0 : this.rootBean.hashCode());
+        result = prime * result
+                + ((this.value == null) ? 0 : this.value.hashCode());
+        result = prime * result
+                + ((this.elementType == null) ? 0 : this.elementType.hashCode());
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     * NOTE: Needed to avoid duplication in the reported violations.
+     * 
+     * @param   obj   the reference object with which to compare.
+     * @return  <code>true</code> if this object is the same as the obj
+     *          argument; <code>false</code> otherwise.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (!(obj instanceof ConstraintViolationImpl<?>)) {
+            return false;
+        }
+
+        ConstraintViolationImpl<?> other = (ConstraintViolationImpl<?>) obj;
+
+        if (this.leafBean == null) {
+            if (other.leafBean != null) {
+                return false;
+            }
+        } else if (!this.leafBean.equals(other.leafBean)) {
+            return false;
+        }
+        
+        if (this.message == null) {
+            if (other.message != null) {
+                return false;
+            }
+        } else if (!this.message.equals(other.message)) {
+            return false;
+        }
+        
+        if (this.propertyPath == null) {
+            if (other.propertyPath != null) {
+                return false;
+            }
+        } else if (!this.propertyPath.equals(other.propertyPath)) {
+            return false;
+        }
+        
+        if (this.rootBean == null) {
+            if (other.rootBean != null) {
+                return false;
+            }
+        } else if (!this.rootBean.equals(other.rootBean)) {
+            return false;
+        }
+        
+        if (this.rootBeanClass != other.rootBeanClass) {
+            return false;
+        }
+        
+        if (this.value == null) {
+            if (other.value != null) {
+                return false;
+            }
+        } else if (!this.value.equals(other.value)) {
+            return false;
+        }
+        
+        if (this.elementType != other.elementType) {
+            return false;
+        }
+        
+        return true;
+    }
+    
+    
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultConstraintValidatorFactory.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultConstraintValidatorFactory.java
new file mode 100644
index 0000000..a67ff7b
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultConstraintValidatorFactory.java
@@ -0,0 +1,50 @@
+/*
+ * 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.bval.jsr303;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.ValidationException;
+
+
+/**
+ * Description: create constraint instances with the default / no-arg constructor <br/>
+ */
+public class DefaultConstraintValidatorFactory implements ConstraintValidatorFactory {
+
+    /**
+     * Instantiate a Constraint.
+     *
+     * @return Returns a new Constraint instance
+     *         The ConstraintFactory is <b>not</b> responsible for calling Constraint#initialize
+     */
+    public <T extends ConstraintValidator<?, ?>> T getInstance(final Class<T> constraintClass)
+    {
+      // 2011-03-27 jw: Do not use PrivilegedAction.
+      // Otherwise any user code would be executed with the privileges of this class.
+      try
+      {
+        return constraintClass.newInstance();
+      }
+      catch (final Exception ex)
+      {
+        throw new ValidationException("Cannot instantiate : " + constraintClass, ex);
+      }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultMessageInterpolator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultMessageInterpolator.java
new file mode 100644
index 0000000..0a341a6
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultMessageInterpolator.java
@@ -0,0 +1,323 @@
+/*
+ * 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.bval.jsr303;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.validation.MessageInterpolator;
+
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.commons.lang3.ArrayUtils;
+
+/**
+ * Description: Resource bundle backed message interpolator.
+ * This message resolver resolve message descriptors
+ * into human-readable messages. It uses ResourceBundles to find the messages.
+ * This class is threadsafe.<br/>
+ */
+public class DefaultMessageInterpolator implements MessageInterpolator {
+    private static final Logger log = Logger.getLogger(DefaultMessageInterpolator.class.getName());
+    private static final String DEFAULT_VALIDATION_MESSAGES =
+          "org.apache.bval.jsr303.ValidationMessages";
+    private static final String USER_VALIDATION_MESSAGES = "ValidationMessages";
+
+    /** Regular expression used to do message interpolation. */
+    private static final Pattern messageParameterPattern =
+          Pattern.compile("(\\{[\\w\\.]+\\})");
+
+    /** The default locale for the current user. */
+    private Locale defaultLocale;
+
+    /** User specified resource bundles hashed against their locale. */
+    private final Map<Locale, ResourceBundle> userBundlesMap =
+          new ConcurrentHashMap<Locale, ResourceBundle>();
+
+    /** Builtin resource bundles hashed against their locale. */
+    private final Map<Locale, ResourceBundle> defaultBundlesMap =
+          new ConcurrentHashMap<Locale, ResourceBundle>();
+
+    /**
+     * Create a new DefaultMessageInterpolator instance.
+     */
+    public DefaultMessageInterpolator() {
+        this(null);
+    }
+
+    /**
+     * Create a new DefaultMessageInterpolator instance.
+     * @param resourceBundle
+     */
+    public DefaultMessageInterpolator(ResourceBundle resourceBundle) {
+
+        defaultLocale = Locale.getDefault();
+
+        if (resourceBundle == null) {
+            ResourceBundle bundle = getFileBasedResourceBundle(defaultLocale);
+            if (bundle != null) {
+                userBundlesMap.put(defaultLocale, bundle);
+            }
+
+        } else {
+            userBundlesMap.put(defaultLocale, resourceBundle);
+        }
+
+        defaultBundlesMap.put(defaultLocale,
+              ResourceBundle.getBundle(DEFAULT_VALIDATION_MESSAGES, defaultLocale));
+    }
+
+    /** {@inheritDoc} */
+    public String interpolate(String message, Context context) {
+        // probably no need for caching, but it could be done by parameters since the map
+        // is immutable and uniquely built per Validation definition, the comparison has to be based on == and not equals though
+        return interpolate(message, context, defaultLocale);
+    }
+
+    /** {@inheritDoc} */
+    public String interpolate(String message, Context context, Locale locale) {
+        return interpolateMessage(message,
+              context.getConstraintDescriptor().getAttributes(), locale);
+    }
+
+    /**
+     * Runs the message interpolation according to algorithm specified in JSR 303.
+     * <br/>
+     * Note:
+     * <br/>
+     * Lookups in user bundles are recursive whereas lookups in default bundle are not!
+     *
+     * @param message              the message to interpolate
+     * @param annotationParameters the parameters of the annotation for which to interpolate this message
+     * @param locale               the <code>Locale</code> to use for the resource bundle.
+     * @return the interpolated message.
+     */
+    private String interpolateMessage(String message,
+                                      Map<String, Object> annotationParameters,
+                                      Locale locale) {
+        ResourceBundle userResourceBundle = findUserResourceBundle(locale);
+        ResourceBundle defaultResourceBundle = findDefaultResourceBundle(locale);
+
+        String userBundleResolvedMessage;
+        String resolvedMessage = message;
+        boolean evaluatedDefaultBundleOnce = false;
+        do {
+            // search the user bundle recursive (step1)
+            userBundleResolvedMessage =
+                  replaceVariables(resolvedMessage, userResourceBundle, locale, true);
+
+            // exit condition - we have at least tried to validate against the default bundle and there were no
+            // further replacements
+            if (evaluatedDefaultBundleOnce &&
+                  !hasReplacementTakenPlace(userBundleResolvedMessage, resolvedMessage)) {
+                break;
+            }
+
+            // search the default bundle non recursive (step2)
+            resolvedMessage = replaceVariables(userBundleResolvedMessage,
+                  defaultResourceBundle, locale, false);
+
+            evaluatedDefaultBundleOnce = true;
+        } while (true);
+
+        // resolve annotation attributes (step 4)
+        resolvedMessage =
+              replaceAnnotationAttributes(resolvedMessage, annotationParameters);
+
+        // curly braces need to be scaped in the original msg, so unescape them now
+        resolvedMessage = resolvedMessage.replace( "\\{", "{" ).replace( "\\}", "}" ).replace( "\\\\", "\\" );
+
+        return resolvedMessage;
+    }
+
+    private boolean hasReplacementTakenPlace(String origMessage, String newMessage) {
+        return !origMessage.equals(newMessage);
+    }
+
+    /**
+     * Search current thread classloader for the resource bundle. If not found, search validator (this) classloader.
+     *
+     * @param locale The locale of the bundle to load.
+     * @return the resource bundle or <code>null</code> if none is found.
+     */
+    private ResourceBundle getFileBasedResourceBundle(Locale locale) {
+        ResourceBundle rb = null;
+        final ClassLoader classLoader = doPrivileged(SecureActions.getContextClassLoader());
+        if (classLoader != null) {
+            rb = loadBundle(classLoader, locale,
+                  USER_VALIDATION_MESSAGES + " not found by thread local classloader");
+        }
+
+        // 2011-03-27 jw: No privileged action required.
+        // A class can always access the classloader of itself and of subclasses.
+        if (rb == null) {
+            rb = loadBundle(
+              getClass().getClassLoader(),
+              locale,
+              USER_VALIDATION_MESSAGES + " not found by validator classloader"
+            );
+        }
+        if (rb != null) {
+            log.log(Level.FINEST, String.format("%s found", USER_VALIDATION_MESSAGES));
+        } else {
+        	log.log(Level.FINEST, String.format("%s not found. Delegating to %s", USER_VALIDATION_MESSAGES, DEFAULT_VALIDATION_MESSAGES));
+        }
+        return rb;
+    }
+
+    private ResourceBundle loadBundle(ClassLoader classLoader, Locale locale,
+                                      String message) {
+        ResourceBundle rb = null;
+        try {
+            rb = ResourceBundle.getBundle(USER_VALIDATION_MESSAGES, locale, classLoader);
+        } catch (MissingResourceException e) {
+            log.fine(message);
+        }
+        return rb;
+    }
+
+    private String replaceVariables(String message, ResourceBundle bundle, Locale locale,
+                                    boolean recurse) {
+        Matcher matcher = messageParameterPattern.matcher(message);
+        StringBuffer sb = new StringBuffer(64);
+        String resolvedParameterValue;
+        while (matcher.find()) {
+            String parameter = matcher.group(1);
+            resolvedParameterValue = resolveParameter(parameter, bundle, locale, recurse);
+
+            matcher.appendReplacement(sb, sanitizeForAppendReplacement(resolvedParameterValue));
+        }
+        matcher.appendTail(sb);
+        return sb.toString();
+    }
+
+    private String replaceAnnotationAttributes(String message,
+                                               Map<String, Object> annotationParameters) {
+        Matcher matcher = messageParameterPattern.matcher(message);
+        StringBuffer sb = new StringBuffer(64);
+        while (matcher.find()) {
+            String resolvedParameterValue;
+            String parameter = matcher.group(1);
+            Object variable = annotationParameters.get(removeCurlyBrace(parameter));
+            if (variable != null) {
+                if (variable.getClass().isArray()) {
+                    resolvedParameterValue = ArrayUtils.toString(variable);
+                } else {
+                    resolvedParameterValue = variable.toString();
+                }
+            } else {
+                resolvedParameterValue = parameter;
+            }
+            matcher.appendReplacement(sb, sanitizeForAppendReplacement(resolvedParameterValue));
+        }
+        matcher.appendTail(sb);
+        return sb.toString();
+    }
+
+    private String resolveParameter(String parameterName, ResourceBundle bundle,
+                                    Locale locale, boolean recurse) {
+        String parameterValue;
+        try {
+            if (bundle != null) {
+                parameterValue = bundle.getString(removeCurlyBrace(parameterName));
+                if (recurse) {
+                    parameterValue =
+                          replaceVariables(parameterValue, bundle, locale, recurse);
+                }
+            } else {
+                parameterValue = parameterName;
+            }
+        } catch (MissingResourceException e) {
+            // return parameter itself
+            parameterValue = parameterName;
+        }
+        return parameterValue;
+    }
+
+    private String removeCurlyBrace(String parameter) {
+        return parameter.substring(1, parameter.length() - 1);
+    }
+
+    private ResourceBundle findDefaultResourceBundle(Locale locale) {
+        ResourceBundle bundle = defaultBundlesMap.get(locale);
+        if (bundle == null)
+        {
+            bundle = ResourceBundle.getBundle(DEFAULT_VALIDATION_MESSAGES, locale);
+            defaultBundlesMap.put(locale, bundle);
+        }
+        return bundle;
+    }
+
+    private ResourceBundle findUserResourceBundle(Locale locale) {
+        ResourceBundle bundle = userBundlesMap.get(locale);
+        if (bundle == null)
+        {
+            bundle = getFileBasedResourceBundle(locale);
+            if (bundle != null) {
+                userBundlesMap.put(locale, bundle);
+            }
+        }
+        return bundle;
+    }
+
+    /**
+     * Set the default locale used by this {@link DefaultMessageInterpolator}.
+     * @param locale
+     */
+    public void setLocale(Locale locale) {
+        defaultLocale = locale;
+    }
+
+    /**
+     * Escapes the string to comply with
+     * {@link Matcher#appendReplacement(StringBuffer, String)} requirements.
+     *
+     * @param src
+     *            The original string.
+     * @return The sanitized string.
+     */
+    private String sanitizeForAppendReplacement(String src) {
+        return src.replace("\\", "\\\\").replace("$", "\\$");
+    }
+
+
+
+    /**
+     * Perform action with AccessController.doPrivileged() if a security manager is installed.
+     *
+     * @param action
+     *  the action to run
+     * @return
+     *  result of the action
+     */
+    private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultValidationProviderResolver.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultValidationProviderResolver.java
new file mode 100644
index 0000000..30e6333
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/DefaultValidationProviderResolver.java
@@ -0,0 +1,98 @@
+/*
+ * 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.bval.jsr303;
+
+
+import javax.validation.ValidationException;
+import javax.validation.ValidationProviderResolver;
+import javax.validation.spi.ValidationProvider;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+public class DefaultValidationProviderResolver implements ValidationProviderResolver {
+
+    //TODO - Spec recommends caching per classloader
+    private static final String SPI_CFG =
+            "META-INF/services/javax.validation.spi.ValidationProvider";
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<ValidationProvider<?>> getValidationProviders() {
+        List<ValidationProvider<?>> providers = new ArrayList<ValidationProvider<?>>();
+        try {
+            // get our classloader
+            ClassLoader cl = Thread.currentThread().getContextClassLoader();
+            if (cl == null)
+                cl = DefaultValidationProviderResolver.class.getClassLoader();
+            // find all service provider cfgs
+            Enumeration<URL> cfgs = cl.getResources(SPI_CFG);
+            while (cfgs.hasMoreElements()) {
+                URL url = cfgs.nextElement();
+                BufferedReader br = null;
+                try {
+                    br = new BufferedReader(new InputStreamReader(url.openStream()), 256);
+                    String line = br.readLine();
+                    // cfgs may contain multiple providers and/or comments
+                    while (line != null) {
+                        line = line.trim();
+                        if (!line.startsWith("#")) {
+                            try {
+                                // try loading the specified class
+                                final Class<?> provider = cl.loadClass(line);
+                                // create an instance to return
+                                ValidationProvider<?> vp =
+                                        AccessController.doPrivileged(new PrivilegedAction<ValidationProvider<?>>() {
+                                            public ValidationProvider<?> run() {
+                                                try {
+                                                    return (ValidationProvider<?>) provider.newInstance();
+                                                } catch (final Exception ex) {
+                                                    throw new ValidationException("Cannot instantiate : " + provider, ex);
+                                                }
+                                            }
+                                        });
+                                 providers.add(vp);
+
+                            } catch (ClassNotFoundException e) {
+                                throw new ValidationException("Failed to load provider " +
+                                        line + " configured in file " + url, e);
+                            }
+                        }
+                        line = br.readLine();
+                    }
+                    br.close();
+                } catch (IOException e) {
+                    throw new ValidationException("Error trying to read " + url, e);
+                } finally {
+                    if (br != null)
+                        br.close();
+                }
+            }
+        } catch (IOException e) {
+            throw new ValidationException("Error trying to read a " + SPI_CFG, e);
+        }
+        // caller must handle the case of no providers found
+        return providers;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ElementDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ElementDescriptorImpl.java
new file mode 100644
index 0000000..7804e5b
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/ElementDescriptorImpl.java
@@ -0,0 +1,141 @@
+/*
+ * 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.bval.jsr303;
+
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.Validation;
+
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Description: MetaData class<br/>
+ */
+public abstract class ElementDescriptorImpl implements ElementDescriptor {
+
+    /**
+     * Get a set of {@link ConstraintDescriptor}s from the specified array of
+     * {@link Validation}s.
+     * 
+     * @param validations
+     * @return {@link ConstraintDescriptor} set
+     */
+    protected static Set<ConstraintDescriptor<?>> getConstraintDescriptors(Validation[] validations) {
+        final Set<ConstraintDescriptor<?>> result = new HashSet<ConstraintDescriptor<?>>(validations.length);
+        for (Validation validation : validations) {
+            if (validation instanceof ConstraintValidation<?>) {
+                result.add((ConstraintValidation<?>) validation);
+            }
+        }
+        return result;
+    }
+
+    /** the MetaBean of this element */
+    protected final MetaBean metaBean;
+
+    /** the raw type of this element */
+    protected final Class<?> elementClass;
+
+    private Set<ConstraintDescriptor<?>> constraintDescriptors;
+
+    /**
+     * Create a new ElementDescriptorImpl instance.
+     * 
+     * @param metaBean
+     * @param elementClass
+     * @param validations
+     */
+    protected ElementDescriptorImpl(MetaBean metaBean, Class<?> elementClass, Validation[] validations) {
+        this.metaBean = metaBean;
+        this.elementClass = elementClass;
+        setConstraintDescriptors(getConstraintDescriptors(validations));
+    }
+
+    /**
+     * Create a new ElementDescriptorImpl instance.
+     * 
+     * @param elementClass
+     * @param validations
+     */
+    protected ElementDescriptorImpl(Class<?> elementClass, Validation[] validations) {
+        this(null, elementClass, validations);
+    }
+
+    /**
+     * {@inheritDoc}
+     * 
+     * @return Statically defined returned type.
+     */
+    public Class<?> getElementClass() {
+        return elementClass;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    public ElementDescriptor.ConstraintFinder findConstraints() {
+        return new ConstraintFinderImpl(metaBean, new HashSet((Set) constraintDescriptors));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<ConstraintDescriptor<?>> getConstraintDescriptors() {
+        return constraintDescriptors.isEmpty() ? Collections.<ConstraintDescriptor<?>> emptySet() : Collections
+            .unmodifiableSet(constraintDescriptors);
+    }
+
+    /**
+     * Get the mutable {@link ConstraintDescriptor} {@link Set}.
+     * 
+     * @return Set of {@link ConstraintDescriptor}
+     */
+    protected Set<ConstraintDescriptor<?>> getMutableConstraintDescriptors() {
+        return constraintDescriptors;
+    }
+
+    /**
+     * {@inheritDoc} return true if at least one constraint declaration is
+     * present on the element.
+     */
+    public boolean hasConstraints() {
+        return !getConstraintDescriptors().isEmpty();
+    }
+
+    /**
+     * Set the constraintDescriptors for this element.
+     * 
+     * @param constraintDescriptors
+     *            to set
+     */
+    public void setConstraintDescriptors(Set<ConstraintDescriptor<?>> constraintDescriptors) {
+        this.constraintDescriptors = constraintDescriptors;
+    }
+
+    /**
+     * Get the model {@link MetaBean} used.
+     * 
+     * @return MetaBean
+     */
+    public MetaBean getMetaBean() {
+        return metaBean;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GraphBeanIdentity.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GraphBeanIdentity.java
new file mode 100644
index 0000000..358bbab
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GraphBeanIdentity.java
@@ -0,0 +1,133 @@
+/*
+ * 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.bval.jsr303;
+
+/**
+ * Class that stores the needed properties to avoid circular paths when
+ * validating an object graph.
+ * <p>
+ * These properties are:
+ * <ul>
+ * <li>The ref of the bean to which the validation would be applied.</li>
+ * <li>The current group being validated.</li>
+ * </ul>
+ * 
+ * FIXME: Owner is currently not used in identity checking, and probably
+ * never will be.  So it is likely to be deleted.
+ * 
+ * @author Carlos Vara
+ */
+public class GraphBeanIdentity {
+    
+    private final Object bean;
+    private final Class<?> group;
+    private final Class<?> owner;
+
+    /**
+     * Create a new GraphBeanIdentity instance.
+     * @param bean
+     * @param group
+     * @param owner
+     */
+    public GraphBeanIdentity(Object bean, Class<?> group, Class<?> owner) {
+        this.bean = bean;
+        this.group = group;
+        this.owner = owner;
+    }
+    
+    /**
+     * Get the bean.
+     * @return Object
+     */
+    public Object getBean() {
+        return bean;
+    }
+
+    /**
+     * Get the group being validated.
+     * @return Class
+     */
+    public Class<?> getGroup() {
+        return group;
+    }
+
+    /**
+     * Get the owning class
+     * @return
+     */
+    public Class<?> getOwner() {
+        return owner;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object obj) {
+
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj == null) {
+            return false;
+        }
+
+        if (!(obj instanceof GraphBeanIdentity)) {
+            return false;
+        }
+
+        GraphBeanIdentity other = (GraphBeanIdentity) obj;
+
+        // Bean ref must be the same
+        if (this.bean != other.bean) {
+            return false;
+        }
+
+        // Group ref must be the same
+        if (this.group != other.group) {
+            return false;
+        }
+        
+//        // Owner ref must be the same
+//        if (this.owner != other.owner) {
+//            return false;
+//        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result
+                + ((this.bean == null) ? 0 : this.bean.hashCode());
+        result = prime * result
+                + ((this.group == null) ? 0 : this.group.hashCode());
+//        result = prime * result
+//                + ((this.owner == null) ? 0 : this.owner.hashCode());
+        return result;
+    }
+
+    
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContext.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContext.java
new file mode 100644
index 0000000..808a51c
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContext.java
@@ -0,0 +1,122 @@
+/*
+ * 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.bval.jsr303;
+
+
+import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.groups.Groups;
+import org.apache.bval.jsr303.util.PathImpl;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.ValidationContext;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+
+/**
+ * Description: JSR-303 {@link ValidationContext} extension. <br/>
+ */
+public interface GroupValidationContext<T>
+      extends ValidationContext<ConstraintValidationListener<T>> {
+    
+    /**
+     * Get the groups of this {@link GroupValidationContext}.
+     * @return the groups in their sequence for validation
+     */
+    Groups getGroups();
+
+    /**
+     * Set the current {@link Group}.
+     * @param group to set
+     */
+    void setCurrentGroup(Group group);
+
+    /**
+     * Get the current {@link Group}.
+     * @return Group
+     */
+    Group getCurrentGroup();
+
+    /**
+     * Get the property path.
+     * @return {@link PathImpl}
+     */
+    PathImpl getPropertyPath();
+
+    /**
+     * Get the root {@link MetaBean}.
+     * @return {@link MetaBean}
+     */
+    MetaBean getRootMetaBean();
+
+    /**
+     * Set the {@link ConstraintValidation}.
+     * @param constraint to set
+     */
+    void setConstraintValidation(ConstraintValidation<?> constraint);
+
+    /**
+     * Get the {@link ConstraintValidation}.
+     * @return {@link ConstraintValidation}
+     */
+    ConstraintValidation<?> getConstraintValidation();
+
+    /**
+     * Get the value being validated.
+     * @return Object
+     */
+    Object getValidatedValue();
+
+    /**
+     * Set a fixed value for the context.
+     * @param value to set
+     */
+    void setFixedValue(Object value);
+
+    /**
+     * Get the message resolver.
+     * @return {@link MessageInterpolator}
+     */
+    MessageInterpolator getMessageResolver();
+
+    /**
+     * Get the {@link TraversableResolver}.
+     * @return {@link TraversableResolver}
+     */
+    TraversableResolver getTraversableResolver();
+
+    /**
+     * Accumulate a validated constraint.
+     * @param constraint
+     * @return true when the constraint for the object in this path was not
+     *         already validated in this context
+     */
+    boolean collectValidated(ConstraintValidator<?, ?> constraint);
+
+    /**
+     * Get the current owning class.
+     * @return Class
+     */
+    Class<?> getCurrentOwner();
+
+    /**
+     * Set the current owning class.
+     * @param currentOwner to set
+     */
+    void setCurrentOwner(Class<?> currentOwner);
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContextImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContextImpl.java
new file mode 100644
index 0000000..3bb5090
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/GroupValidationContextImpl.java
@@ -0,0 +1,291 @@
+/*
+ * 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.bval.jsr303;
+
+import org.apache.bval.BeanValidationContext;
+import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.groups.Groups;
+import org.apache.bval.jsr303.resolver.CachingTraversableResolver;
+import org.apache.bval.jsr303.util.NodeImpl;
+import org.apache.bval.jsr303.util.PathImpl;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.util.AccessStrategy;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Description: instance per validation process, not thread-safe<br/>
+ */
+final class GroupValidationContextImpl<T> extends BeanValidationContext<ConstraintValidationListener<T>> implements
+    GroupValidationContext<T>, MessageInterpolator.Context {
+
+    private final MessageInterpolator messageResolver;
+    private final PathImpl path;
+    private final MetaBean rootMetaBean;
+
+    /**
+     * the groups in the sequence of validation to take place
+     */
+    private Groups groups;
+    /**
+     * the current group during the validation process
+     */
+    private Group currentGroup;
+
+    private Class<?> currentOwner;
+
+    /**
+     * contains the validation constraints that have already been processed
+     * during this validation routine (as part of a previous group match)
+     */
+    private HashSet<ConstraintValidatorIdentity> validatedConstraints = new HashSet<ConstraintValidatorIdentity>();
+
+    private ConstraintValidation<?> constraintValidation;
+    private final TraversableResolver traversableResolver;
+
+    /**
+     * Create a new GroupValidationContextImpl instance.
+     * 
+     * @param listener
+     * @param aMessageResolver
+     * @param traversableResolver
+     * @param rootMetaBean
+     */
+    public GroupValidationContextImpl(ConstraintValidationListener<T> listener, MessageInterpolator aMessageResolver,
+        TraversableResolver traversableResolver, MetaBean rootMetaBean) {
+        // inherited variable 'validatedObjects' is of type:
+        // HashMap<GraphBeanIdentity, Set<PathImpl>> in this class
+        super(listener, new HashMap<GraphBeanIdentity, Set<PathImpl>>());
+        this.messageResolver = aMessageResolver;
+        this.traversableResolver = CachingTraversableResolver.cacheFor(traversableResolver);
+        this.rootMetaBean = rootMetaBean;
+        this.path = PathImpl.create(null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setCurrentIndex(Integer index) {
+        NodeImpl leaf = path.getLeafNode();
+        if (leaf.getName() == null) {
+            leaf.setIndex(index);
+        } else {
+            path.addNode(NodeImpl.atIndex(index));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setCurrentKey(Object key) {
+        NodeImpl leaf = path.getLeafNode();
+        if (leaf.getName() == null) {
+            leaf.setKey(key);
+        } else {
+            path.addNode(NodeImpl.atKey(key));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void moveDown(MetaProperty prop, AccessStrategy access) {
+        path.addProperty(prop.getName());
+        super.moveDown(prop, access);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void moveUp(Object bean, MetaBean metaBean) {
+        NodeImpl leaf = path.getLeafNode();
+        if (leaf.isInIterable() && leaf.getName() != null) {
+            leaf.setName(null);
+        } else {
+            path.removeLeafNode();
+        }
+        super.moveUp(bean, metaBean); // call super!
+    }
+
+    /**
+     * {@inheritDoc} Here, state equates to bean identity + group.
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean collectValidated() {
+
+        // Combination of bean+group+owner (owner is currently ignored)
+        GraphBeanIdentity gbi = new GraphBeanIdentity(getBean(), getCurrentGroup().getGroup(), getCurrentOwner());
+
+        Set<PathImpl> validatedPathsForGBI = (Set<PathImpl>) validatedObjects.get(gbi);
+        if (validatedPathsForGBI == null) {
+            validatedPathsForGBI = new HashSet<PathImpl>();
+            validatedObjects.put(gbi, validatedPathsForGBI);
+        }
+
+        // If any of the paths is a subpath of the current path, there is a
+        // circular dependency, so return false
+        for (PathImpl validatedPath : validatedPathsForGBI) {
+            if (path.isSubPathOf(validatedPath)) {
+                return false;
+            }
+        }
+
+        // Else, add the currentPath to the set of validatedPaths
+        validatedPathsForGBI.add(PathImpl.copy(path));
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean collectValidated(ConstraintValidator<?, ?> constraint) {
+        ConstraintValidatorIdentity cvi = new ConstraintValidatorIdentity(getBean(), getPropertyPath(), constraint);
+        return this.validatedConstraints.add(cvi);
+    }
+
+    /**
+     * Reset the validated constraints.
+     */
+    public void resetValidatedConstraints() {
+        validatedConstraints.clear();
+    }
+
+    /**
+     * {@inheritDoc} If an associated object is validated, add the association
+     * field or JavaBeans property name and a dot ('.') as a prefix to the
+     * previous rules. uses prop[index] in property path for elements in
+     * to-many-relationships.
+     * 
+     * @return the path in dot notation
+     */
+    public PathImpl getPropertyPath() {
+        PathImpl currentPath = PathImpl.copy(path);
+        if (getMetaProperty() != null) {
+            currentPath.addProperty(getMetaProperty().getName());
+        }
+        return currentPath;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MetaBean getRootMetaBean() {
+        return rootMetaBean;
+    }
+
+    /**
+     * Set the Groups.
+     * 
+     * @param groups
+     */
+    public void setGroups(Groups groups) {
+        this.groups = groups;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Groups getGroups() {
+        return groups;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Group getCurrentGroup() {
+        return currentGroup;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setCurrentGroup(Group currentGroup) {
+        this.currentGroup = currentGroup;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setConstraintValidation(ConstraintValidation<?> constraint) {
+        constraintValidation = constraint;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintValidation<?> getConstraintValidation() {
+        return constraintValidation;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintDescriptor<?> getConstraintDescriptor() {
+        return constraintValidation;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object getValidatedValue() {
+        if (getMetaProperty() != null) {
+            return getPropertyValue(constraintValidation.getAccess());
+        } else {
+            return getBean();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MessageInterpolator getMessageResolver() {
+        return messageResolver;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public TraversableResolver getTraversableResolver() {
+        return traversableResolver;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Class<?> getCurrentOwner() {
+        return this.currentOwner;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setCurrentOwner(Class<?> currentOwner) {
+        this.currentOwner = currentOwner;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/IncompatiblePropertyValueException.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/IncompatiblePropertyValueException.java
new file mode 100644
index 0000000..8c67751
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/IncompatiblePropertyValueException.java
@@ -0,0 +1,69 @@
+/*
+ * 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.bval.jsr303;
+
+import javax.validation.ValidationException;
+
+/**
+ * Internal exception thrown when trying to validate a value for a property for which it is not assignment-compatible.
+ * 
+ * @version $Rev: 1031833 $ $Date: 2010-11-05 16:53:03 -0500 (Fri, 05 Nov 2010) $
+ * 
+ * @author Matt Benson
+ */
+public class IncompatiblePropertyValueException extends ValidationException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Create a new {@link IncompatiblePropertyValueException} instance.
+     * 
+     * @param message
+     */
+    public IncompatiblePropertyValueException(String message) {
+        super(message);
+    }
+
+    /**
+     * Create a new IncompatiblePropertyValueException instance.
+     */
+    public IncompatiblePropertyValueException() {
+        super();
+    }
+
+    /**
+     * Create a new IncompatiblePropertyValueException instance.
+     * 
+     * @param message
+     * @param cause
+     */
+    public IncompatiblePropertyValueException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Create a new IncompatiblePropertyValueException instance.
+     * 
+     * @param cause
+     */
+    public IncompatiblePropertyValueException(Throwable cause) {
+        super(cause);
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303Features.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303Features.java
new file mode 100644
index 0000000..4bd6990
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303Features.java
@@ -0,0 +1,61 @@
+/*
+ * 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.bval.jsr303;
+
+import org.apache.bval.model.Features;
+
+/**
+ * Description: Contains MetaBean feature keys of additional features used in the implementation
+ * of JSR303<br/>
+ *
+ * @see org.apache.bval.model.FeaturesCapable
+ * @see org.apache.bval.model.Features
+ */
+public interface Jsr303Features {
+    /**
+     * JSR303 Property features
+     */
+    interface Property extends Features.Property {
+        /** INFO: cached PropertyDescriptorImpl of the property */
+        String PropertyDescriptor = "PropertyDescriptor";
+        /**
+         * INFO: Class[] with the groups to validate a REF_CASCADE
+         */
+        String REF_GROUPS = "refGroups";
+    }
+
+    /**
+     * JSR303 bean features
+     */
+    interface Bean extends Features.Bean {
+        /**
+         * INFO: List of Group(Class) for {@link javax.validation.GroupSequence#value()}
+         * (redefined default group)
+         **/
+        String GROUP_SEQUENCE = "GroupSequence";
+
+//        INFO: cached sorted Array with ValidationEntries 
+//        String VALIDATION_SEQUENCE = "ValidationSequence";
+
+        /**
+         * INFO: cached BeanDescriptorImpl of the bean
+         */
+        String BEAN_DESCRIPTOR = "BeanDescriptor";
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java
new file mode 100644
index 0000000..296e8b1
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/Jsr303MetaBeanFactory.java
@@ -0,0 +1,317 @@
+/*
+ * 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.bval.jsr303;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.validation.Constraint;
+import javax.validation.GroupDefinitionException;
+import javax.validation.GroupSequence;
+import javax.validation.ValidationException;
+import javax.validation.groups.Default;
+
+import org.apache.bval.MetaBeanFactory;
+import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.util.ClassHelper;
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.jsr303.xml.MetaConstraint;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.FieldAccess;
+import org.apache.bval.util.MethodAccess;
+
+/**
+ * Description: process the class annotations for JSR303 constraint validations to build the MetaBean with information
+ * from annotations and JSR303 constraint mappings (defined in xml)<br/>
+ */
+public class Jsr303MetaBeanFactory implements MetaBeanFactory {
+    /** Shared log instance */
+    // of dubious utility as it's static :/
+    protected static final Logger log = Logger.getLogger(Jsr303MetaBeanFactory.class.getName());
+
+    /** {@link ApacheFactoryContext} used */
+    protected final ApacheFactoryContext factoryContext;
+
+    /**
+     * {@link AnnotationProcessor} used.
+     */
+    protected AnnotationProcessor annotationProcessor;
+
+    /**
+     * Create a new Jsr303MetaBeanFactory instance.
+     * 
+     * @param factoryContext
+     */
+    public Jsr303MetaBeanFactory(ApacheFactoryContext factoryContext) {
+        this.factoryContext = factoryContext;
+        this.annotationProcessor = new AnnotationProcessor(factoryContext);
+    }
+
+    /**
+     * {@inheritDoc} Add the validation features to the metabean that come from JSR303 annotations in the beanClass.
+     */
+    public void buildMetaBean(MetaBean metabean) {
+        try {
+            final Class<?> beanClass = metabean.getBeanClass();
+            processGroupSequence(beanClass, metabean);
+
+            // process class, superclasses and interfaces
+            List<Class<?>> classSequence = new ArrayList<Class<?>>();
+            ClassHelper.fillFullClassHierarchyAsList(classSequence, beanClass);
+
+            // start with superclasses and go down the hierarchy so that
+            // the child classes are processed last to have the chance to
+            // overwrite some declarations
+            // of their superclasses and that they see what they inherit at the
+            // time of processing
+            for (int i = classSequence.size() - 1; i >= 0; i--) {
+                Class<?> eachClass = classSequence.get(i);
+                processClass(eachClass, metabean);
+                processGroupSequence(eachClass, metabean, "{GroupSequence:" + eachClass.getCanonicalName() + "}");
+            }
+
+        } catch (IllegalAccessException e) {
+            throw new IllegalArgumentException(e);
+        } catch (InvocationTargetException e) {
+            throw new IllegalArgumentException(e.getTargetException());
+        }
+    }
+
+    /**
+     * Process class annotations, field and method annotations.
+     * 
+     * @param beanClass
+     * @param metabean
+     * @throws IllegalAccessException
+     * @throws InvocationTargetException
+     */
+    private void processClass(Class<?> beanClass, MetaBean metabean) throws IllegalAccessException,
+        InvocationTargetException {
+
+        // if NOT ignore class level annotations
+        if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(beanClass)) {
+            annotationProcessor.processAnnotations(null, beanClass, beanClass, null, new AppendValidationToMeta(
+                metabean));
+        }
+
+        final Field[] fields = doPrivileged(SecureActions.getDeclaredFields(beanClass));
+        for (Field field : fields) {
+            MetaProperty metaProperty = metabean.getProperty(field.getName());
+            // create a property for those fields for which there is not yet a
+            // MetaProperty
+            if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(field)) {
+                AccessStrategy access = new FieldAccess(field);
+                boolean create = metaProperty == null;
+                if (create) {
+                    metaProperty = addMetaProperty(metabean, access);
+                }
+                if (!annotationProcessor.processAnnotations(metaProperty, beanClass, field, access,
+                    new AppendValidationToMeta(metaProperty)) && create) {
+                    metabean.putProperty(metaProperty.getName(), null);
+                }
+            }
+        }
+        final Method[] methods = doPrivileged(SecureActions.getDeclaredMethods(beanClass));
+        for (Method method : methods) {
+            String propName = null;
+            if (method.getParameterTypes().length == 0) {
+                propName = MethodAccess.getPropertyName(method);
+            }
+            if (propName != null) {
+                if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(method)) {
+                    AccessStrategy access = new MethodAccess(propName, method);
+                    MetaProperty metaProperty = metabean.getProperty(propName);
+                    boolean create = metaProperty == null;
+                    // create a property for those methods for which there is
+                    // not yet a MetaProperty
+                    if (create) {
+                        metaProperty = addMetaProperty(metabean, access);
+                    }
+                    if (!annotationProcessor.processAnnotations(metaProperty, beanClass, method, access,
+                        new AppendValidationToMeta(metaProperty)) && create) {
+                        metabean.putProperty(propName, null);
+                    }
+                }
+            } else if (hasValidationConstraintsDefined(method)) {
+                throw new ValidationException("Property " + method.getName() + " does not follow javabean conventions.");
+            }
+        }
+
+        addXmlConstraints(beanClass, metabean);
+    }
+
+    /**
+     * Learn whether a given Method has validation constraints defined via JSR303 annotations.
+     * 
+     * @param method
+     * @return <code>true</code> if constraints detected
+     */
+    protected boolean hasValidationConstraintsDefined(Method method) {
+        for (Annotation annot : method.getDeclaredAnnotations()) {
+            if (hasValidationConstraintsDefined(annot)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean hasValidationConstraintsDefined(Annotation annot) {
+        // If it is annotated with @Constraint
+        if (annot.annotationType().getAnnotation(Constraint.class) != null) {
+            return true;
+        }
+
+        // Check whether it is a multivalued constraint:
+        if (ConstraintAnnotationAttributes.VALUE.isDeclaredOn(annot.annotationType())) {
+            Annotation[] children = ConstraintAnnotationAttributes.VALUE.getValue(annot);
+            if (children != null) {
+                for (Annotation child : children) {
+                    if (hasValidationConstraintsDefined(child)) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Add cascade validation and constraints from xml mappings
+     * 
+     * @param beanClass
+     * @param metabean
+     * @throws IllegalAccessException
+     * @throws InvocationTargetException
+     */
+    private void addXmlConstraints(Class<?> beanClass, MetaBean metabean) throws IllegalAccessException,
+        InvocationTargetException {
+        for (MetaConstraint<?, ? extends Annotation> meta : factoryContext.getFactory().getMetaConstraints(beanClass)) {
+            MetaProperty metaProperty;
+            AccessStrategy access = meta.getAccessStrategy();
+            boolean create = false;
+            if (access == null) { // class level
+                metaProperty = null;
+            } else { // property level
+                metaProperty = metabean.getProperty(access.getPropertyName());
+                create = metaProperty == null;
+                if (create) {
+                    metaProperty = addMetaProperty(metabean, access);
+                }
+            }
+            if (!annotationProcessor.processAnnotation(meta.getAnnotation(), metaProperty, beanClass,
+                meta.getAccessStrategy(), new AppendValidationToMeta(metaProperty == null ? metabean : metaProperty))
+                && create) {
+                metabean.putProperty(access.getPropertyName(), null);
+            }
+        }
+        for (AccessStrategy access : factoryContext.getFactory().getValidAccesses(beanClass)) {
+            MetaProperty metaProperty = metabean.getProperty(access.getPropertyName());
+            boolean create = metaProperty == null;
+            if (create) {
+                metaProperty = addMetaProperty(metabean, access);
+            }
+            if (!annotationProcessor.addAccessStrategy(metaProperty, access) && create) {
+                metabean.putProperty(access.getPropertyName(), null);
+            }
+        }
+    }
+
+    private void processGroupSequence(Class<?> beanClass, MetaBean metabean) {
+        processGroupSequence(beanClass, metabean, Jsr303Features.Bean.GROUP_SEQUENCE);
+    }
+
+    private void processGroupSequence(Class<?> beanClass, MetaBean metabean, String key) {
+        GroupSequence annotation = beanClass.getAnnotation(GroupSequence.class);
+        List<Group> groupSeq = metabean.getFeature(key);
+        if (groupSeq == null) {
+            groupSeq = new ArrayList<Group>(annotation == null ? 1 : annotation.value().length);
+            metabean.putFeature(key, groupSeq);
+        }
+        Class<?>[] groupClasses = factoryContext.getFactory().getDefaultSequence(beanClass);
+        if (groupClasses == null || groupClasses.length == 0) {
+            if (annotation == null) {
+                groupSeq.add(Group.DEFAULT);
+                return;
+            } else {
+                groupClasses = annotation.value();
+            }
+        }
+        boolean containsDefault = false;
+        for (Class<?> groupClass : groupClasses) {
+            if (groupClass.getName().equals(beanClass.getName())) {
+                groupSeq.add(Group.DEFAULT);
+                containsDefault = true;
+            } else if (groupClass.getName().equals(Default.class.getName())) {
+                throw new GroupDefinitionException("'Default.class' must not appear in @GroupSequence! Use '"
+                    + beanClass.getSimpleName() + ".class' instead.");
+            } else {
+                groupSeq.add(new Group(groupClass));
+            }
+        }
+        if (!containsDefault) {
+            throw new GroupDefinitionException("Redefined default group sequence must contain " + beanClass.getName());
+        }
+        log.log(Level.FINEST, String.format("Default group sequence for bean %s is: %s", beanClass.getName(), groupSeq));
+    }
+
+    /**
+     * Add a {@link MetaProperty} to a {@link MetaBean}.
+     * @param parentMetaBean
+     * @param access
+     * @return the created {@link MetaProperty}
+     */
+    public static MetaProperty addMetaProperty(MetaBean parentMetaBean, AccessStrategy access) {
+        final MetaProperty result = new MetaProperty();
+        final String name = access.getPropertyName();
+        result.setName(name);
+        result.setType(access.getJavaType());
+        parentMetaBean.putProperty(name, result);
+        return result;
+    }
+
+
+
+
+    /**
+     * Perform action with AccessController.doPrivileged() if a security manager is installed.
+     *
+     * @param action
+     *  the action to run
+     * @return
+     *  result of the action
+     */
+    private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/PropertyDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/PropertyDescriptorImpl.java
new file mode 100644
index 0000000..20197e4
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/PropertyDescriptorImpl.java
@@ -0,0 +1,83 @@
+/*
+ * 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.bval.jsr303;
+
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaProperty;
+
+import javax.validation.metadata.PropertyDescriptor;
+
+/**
+ * Description: {@link PropertyDescriptor} implementation.<br/>
+ */
+class PropertyDescriptorImpl extends ElementDescriptorImpl implements PropertyDescriptor {
+    private boolean cascaded;
+    private String propertyPath;
+
+    /**
+     * Create a new PropertyDescriptorImpl instance.
+     * 
+     * @param property
+     */
+    PropertyDescriptorImpl(MetaProperty property) {
+        super(property.getParentMetaBean(), property.getTypeClass(), property.getValidations());
+        setCascaded(property.getMetaBean() != null || property.getFeature(Features.Property.REF_CASCADE) != null);
+        setPropertyPath(property.getName());
+    }
+
+    /**
+     * Set whether the referenced property is cascaded.
+     * 
+     * @param cascaded
+     */
+    public void setCascaded(boolean cascaded) {
+        this.cascaded = cascaded;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isCascaded() {
+        return cascaded;
+    }
+
+    /**
+     * Set the referenced property path.
+     * 
+     * @param propertyPath
+     */
+    public void setPropertyPath(String propertyPath) {
+        this.propertyPath = propertyPath;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getPropertyName() {
+        return propertyPath;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return "PropertyDescriptorImpl{" + "returnType=" + elementClass + ", propertyPath='" + propertyPath + '\''
+            + '}';
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/UnknownPropertyException.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/UnknownPropertyException.java
new file mode 100644
index 0000000..dabcf6e
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/UnknownPropertyException.java
@@ -0,0 +1,67 @@
+/*
+ * 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.bval.jsr303;
+
+import javax.validation.ValidationException;
+
+/**
+ * Internal exception thrown when trying to access a property that doesn't exist
+ * in a bean.
+ * 
+ * @version $Rev$ $Date$
+ * 
+ * @author Carlos Vara
+ */
+public class UnknownPropertyException extends ValidationException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Create a new UnknownPropertyException instance.
+     * @param message
+     */
+    public UnknownPropertyException(String message) {
+        super(message);
+    }
+
+    /**
+     * Create a new UnknownPropertyException instance.
+     */
+    public UnknownPropertyException() {
+        super();
+    }
+
+    /**
+     * Create a new UnknownPropertyException instance.
+     * @param message
+     * @param cause
+     */
+    public UnknownPropertyException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Create a new UnknownPropertyException instance.
+     * @param cause
+     */
+    public UnknownPropertyException(Throwable cause) {
+        super(cause);
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/AppendValidationToList.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/AppendValidationToList.java
new file mode 100644
index 0000000..602f27c
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/AppendValidationToList.java
@@ -0,0 +1,55 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+
+import org.apache.bval.jsr303.AppendValidation;
+import org.apache.bval.jsr303.BaseAppendValidation;
+import org.apache.bval.jsr303.ConstraintValidation;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Description: {@link AppendValidation} implementation that acts as an intermediate
+ * cache of validations for further processing.<br/>
+ */
+public class AppendValidationToList extends BaseAppendValidation {
+    private final List<ConstraintValidation<?>> validations = new ArrayList<ConstraintValidation<?>>();
+
+    /**
+     * Create a new AppendValidationToList instance.
+     */
+    public AppendValidationToList() {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T extends Annotation> void performAppend(ConstraintValidation<T> validation) {
+        validations.add(validation);
+    }
+
+    /**
+     * Get the list of cached validations.
+     * @return {@link List} of {@link ConstraintValidation}
+     */
+    public List<ConstraintValidation<?>> getValidations() {
+        return validations;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ConstructorDescriptor.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ConstructorDescriptor.java
new file mode 100644
index 0000000..6e273fd
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ConstructorDescriptor.java
@@ -0,0 +1,38 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+import javax.validation.metadata.ElementDescriptor;
+import java.util.List;
+
+/**
+ * Description: This class will disappear when such
+ * functionality is part of the JSR303 specification.<br/>
+ */
+public interface ConstructorDescriptor extends ElementDescriptor {
+    /**
+     * Get the list of {@link ParameterDescriptor}s.
+     * @return {@link List} of {@link ParameterDescriptor}
+     */
+    List<ParameterDescriptor> getParameterDescriptors(); //index aligned
+
+    /**
+     * Learn whether the referenced constructor should be validated.
+     * @return
+     */
+    boolean isCascaded();
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ConstructorDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ConstructorDescriptorImpl.java
new file mode 100644
index 0000000..775c83d
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ConstructorDescriptorImpl.java
@@ -0,0 +1,74 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+
+import org.apache.bval.jsr303.ElementDescriptorImpl;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.Validation;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Description: {@link ConstructorDescriptor} implementation.<br/>
+ */
+public class ConstructorDescriptorImpl extends ElementDescriptorImpl
+      implements ConstructorDescriptor, ProcedureDescriptor {
+    private final List<ParameterDescriptor> parameterDescriptors = new ArrayList<ParameterDescriptor>();
+    private boolean cascaded;
+
+    /**
+     * Create a new ConstructorDescriptorImpl instance.
+     * @param metaBean
+     * @param validations
+     */
+    protected ConstructorDescriptorImpl(MetaBean metaBean, Validation[] validations) {
+        super(metaBean, metaBean.getBeanClass(), validations);
+    }
+
+    /**
+     * Create a new ConstructorDescriptorImpl instance.
+     * @param elementClass
+     * @param validations
+     */
+    protected ConstructorDescriptorImpl(Class<?> elementClass, Validation[] validations) {
+        super(elementClass, validations);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isCascaded() {
+        return cascaded;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setCascaded(boolean cascaded) {
+        this.cascaded = cascaded;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<ParameterDescriptor> getParameterDescriptors() //index aligned
+    {
+        return parameterDescriptors;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodBeanDescriptor.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodBeanDescriptor.java
new file mode 100644
index 0000000..5cac52e
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodBeanDescriptor.java
@@ -0,0 +1,55 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+import javax.validation.metadata.BeanDescriptor;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+/**
+ * Description: Provides method/constructor-related constraint information
+ * for a type.  This class will disappear when such
+ * functionality is part of the JSR303 specification.<br/>
+ */
+public interface MethodBeanDescriptor extends BeanDescriptor {
+    /**
+     * Get the constraints that apply to a particular method.
+     * @param method
+     * @return {@link MethodDescriptor}
+     */
+    MethodDescriptor getConstraintsForMethod(Method method);
+
+    /**
+     * Get the constraints that apply to a particular constructor.
+     * @param constructor
+     * @return {@link ConstructorDescriptor}
+     */
+    ConstructorDescriptor getConstraintsForConstructor(Constructor<?> constructor);
+
+    /**
+     * Get the set of constrained methods.
+     * @return {@link Set} of {@link MethodDescriptor}
+     */
+    Set<MethodDescriptor> getConstrainedMethods();
+
+    /**
+     * Get the set of constrained constructors.
+     * @return {@link Set} of {@link ConstructorDescriptor}
+     */
+    Set<ConstructorDescriptor> getConstrainedConstructors();
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodBeanDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodBeanDescriptorImpl.java
new file mode 100644
index 0000000..e8a482d
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodBeanDescriptorImpl.java
@@ -0,0 +1,131 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+import org.apache.bval.jsr303.ApacheFactoryContext;
+import org.apache.bval.jsr303.BeanDescriptorImpl;
+import org.apache.bval.model.MetaBean;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Description: {@link MethodBeanDescriptor} implementation.<br/>
+ */
+class MethodBeanDescriptorImpl extends BeanDescriptorImpl implements MethodBeanDescriptor {
+    private Map<Method, MethodDescriptor> methodConstraints;
+    private Map<Constructor<?>, ConstructorDescriptor> constructorConstraints;
+
+    /**
+     * Create a new MethodBeanDescriptorImpl instance.
+     * 
+     * @param factoryContext
+     * @param metaBean
+     */
+    protected MethodBeanDescriptorImpl(ApacheFactoryContext factoryContext, MetaBean metaBean) {
+        super(factoryContext, metaBean);
+    }
+
+    /**
+     * Set the map of method constraints for this bean.
+     * 
+     * @param methodConstraints
+     */
+    public void setMethodConstraints(Map<Method, MethodDescriptor> methodConstraints) {
+        this.methodConstraints = methodConstraints;
+    }
+
+    /**
+     * Set the map of constructor constraints for this bean.
+     * 
+     * @param constructorConstraints
+     */
+    public void setConstructorConstraints(Map<Constructor<?>, ConstructorDescriptor> constructorConstraints) {
+        this.constructorConstraints = constructorConstraints;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public MethodDescriptor getConstraintsForMethod(Method method) {
+        return methodConstraints.get(method);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstructorDescriptor getConstraintsForConstructor(Constructor<?> constructor) {
+        return constructorConstraints.get(constructor);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<MethodDescriptor> getConstrainedMethods() {
+        return new HashSet<MethodDescriptor>(methodConstraints.values());
+    }
+
+    /**
+     * Add a {@link MethodDescriptor} to this {@link MethodBeanDescriptorImpl}.
+     * 
+     * @param method
+     * @param desc
+     */
+    public void putMethodDescriptor(Method method, MethodDescriptor desc) {
+        methodConstraints.put(method, desc);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<ConstructorDescriptor> getConstrainedConstructors() {
+        return new HashSet<ConstructorDescriptor>(this.constructorConstraints.values());
+    }
+
+    /**
+     * Add a {@link ConstructorDescriptor} to this
+     * {@link MethodBeanDescriptorImpl}.
+     * 
+     * @param cons
+     * @param desc
+     */
+    public void putConstructorDescriptor(Constructor<?> cons, ConstructorDescriptor desc) {
+        constructorConstraints.put(cons, desc);
+    }
+
+    /**
+     * Get the configured method constraints.
+     * 
+     * @return {@link Map} of {@link Method} : {@link MethodDescriptor}
+     */
+    public Map<Method, MethodDescriptor> getMethodConstraints() {
+        return methodConstraints;
+    }
+
+    /**
+     * Get the configured constructor constraints.
+     * 
+     * @return {@link Map} of {@link Constructor} :
+     *         {@link ConstructorDescriptor}
+     */
+    public Map<Constructor<?>, ConstructorDescriptor> getConstructorConstraints() {
+        return constructorConstraints;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodDescriptor.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodDescriptor.java
new file mode 100644
index 0000000..71a4289
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodDescriptor.java
@@ -0,0 +1,39 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+import javax.validation.metadata.ElementDescriptor;
+import java.util.List;
+
+/**
+ * Description: This class will disappear when such
+ * functionality is part of the JSR303 specification.<br/>
+ */
+public interface MethodDescriptor extends ElementDescriptor {
+    /**
+     * Get the {@link ParameterDescriptor}s for this {@link MethodDescriptor}.
+     * @return {@link List} of {@link ParameterDescriptor}
+     */
+    List<ParameterDescriptor> getParameterDescriptors(); //index aligned
+
+    /**
+     * Learn whether the referenced method should be validated.
+     * @return boolean
+     */
+    boolean isCascaded();
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodDescriptorImpl.java
new file mode 100644
index 0000000..4281414
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodDescriptorImpl.java
@@ -0,0 +1,84 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+import org.apache.bval.jsr303.ConstraintValidation;
+import org.apache.bval.jsr303.ElementDescriptorImpl;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.Validation;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Description: {@link MethodDescriptor} implementation.<br/>
+ */
+public class MethodDescriptorImpl extends ElementDescriptorImpl implements MethodDescriptor, ProcedureDescriptor {
+    private final List<ParameterDescriptor> parameterDescriptors = new ArrayList<ParameterDescriptor>();
+    private boolean cascaded;
+
+    /**
+     * Create a new MethodDescriptorImpl instance.
+     * 
+     * @param metaBean
+     * @param validations
+     */
+    protected MethodDescriptorImpl(MetaBean metaBean, Validation[] validations) {
+        super(metaBean, metaBean.getClass(), validations);
+    }
+
+    /**
+     * Create a new MethodDescriptorImpl instance.
+     * 
+     * @param elementClass
+     * @param validations
+     */
+    protected MethodDescriptorImpl(Class<?> elementClass, Validation[] validations) {
+        super(elementClass, validations);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List<ParameterDescriptor> getParameterDescriptors() {
+        // index aligned
+        return parameterDescriptors;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isCascaded() {
+        return cascaded;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setCascaded(boolean cascaded) {
+        this.cascaded = cascaded;
+    }
+
+    /**
+     * Add the specified validations to this {@link MethodDescriptorImpl}.
+     * @param validations
+     */
+    void addValidations(Collection<ConstraintValidation<?>> validations) {
+        getMutableConstraintDescriptors().addAll(validations);
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidator.java
new file mode 100644
index 0000000..8f35642
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidator.java
@@ -0,0 +1,119 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validator;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+/**
+ * Description: Appendix C. Proposal for method-level validation.
+ * This interface contains the APIs added to javax.validation.Validator.
+ * It can be removed as soon as the Validator interface contains these methods.
+ * The extension is not a part of the JSR303 core specification yet, but could
+ * be in a future revision.<br/>
+ * You can access the extension via the use of the Validator.unwrap() method.<br/>
+ */
+public interface MethodValidator extends Validator {
+    /**
+     * Validate each parameter value based on the constraints described on
+     * the parameters of <code>method</code>.
+     *
+     * @param clazz           class hosting the method
+     * @param method          the method whose parameters are currectly validated
+     * @param parameterValues the parameter values passed to the method for invocation
+     * @param groups          groups targeted for validation
+     * @return set of constraint violations
+     * @throws IllegalArgumentException if the method does not belong to <code>T</code>
+     *                                  or if the Object[] does not match the method signature
+     */
+    <T> Set<ConstraintViolation<T>> validateParameters(Class<T> clazz, Method method,
+                                                       Object[] parameterValues,
+                                                       Class<?>... groups);
+
+    /**
+     * Validate the parameter value based on the constraints described on
+     * the parameterIndex-th parameter of <code>method</code>.
+     *
+     * @param clazz          class hosting the method
+     * @param method         the method whose parameters are currectly validated
+     * @param parameterValue the parameter value passed to the parameterIndex-t parameter of method
+     * @param parameterIndex parameter index of the parameter validated in method
+     * @param groups         groups targeted for validation
+     * @return set of constraint violations
+     * @throws IllegalArgumentException if the method does not belong to <code>T</code>
+     *                                  or if parameterIndex is out of bound
+     */
+    <T> Set<ConstraintViolation<T>> validateParameter(Class<T> clazz, Method method,
+                                                   Object parameterValue,
+                                                   int parameterIndex,
+                                                   Class<?>... groups);
+
+    /**
+     * Validate each parameter value based on the constraints described on
+     * <code>method</code>.
+     *
+     * @param clazz         class hosting the method
+     * @param method        the method whose result is validated
+     * @param returnedValue the value returned by the method invocation
+     * @param groups        groups targeted for validation
+     * @return set of constraint violations
+     * @throws IllegalArgumentException if the method does not belong to <code>T</code>
+     */
+    <T> Set<ConstraintViolation<T>> validateReturnedValue(Class<T> clazz, Method method,
+                                                       Object returnedValue,
+                                                       Class<?>... groups);
+
+    /**
+     * Validate each parameter value based on the constraints described on
+     * the parameters of <code>constructor</code>.
+     *
+     * @param clazz           class hosting the constructor
+     * @param constructor     the constructor whose parameters are correctly validated
+     * @param parameterValues the parameter values passed to the constructor for invocation
+     * @param groups          groups targeted for validation
+     * @return set of constraint violations
+     * @throws IllegalArgumentException if the constructor does not belong to <code>T</code>
+     *                                  or if the Object[] does not match the constructor signature
+     */
+    <T> Set<ConstraintViolation<T>> validateParameters(Class<T> clazz,
+                                                    Constructor<T> constructor,
+                                                    Object[] parameterValues,
+                                                    Class<?>... groups);
+
+    /**
+     * Validate the parameter value based on the constraints described on
+     * the parameterIndex-th parameter of <code>constructor</code>.
+     *
+     * @param clazz          class hosting the constructor
+     * @param constructor    the method whose parameters are correctly validated
+     * @param parameterValue the parameter value passed to the
+     *                       parameterIndex-th parameter of constructor
+     * @param parameterIndex parameter index of the parameter validated in constructor
+     * @param groups         groups targeted for validation
+     * @return set of constraint violations
+     * @throws IllegalArgumentException if the constructor does not belong to <code>T</code>
+     *                                  or if prameterIndex is out of bound
+     */
+    <T> Set<ConstraintViolation<T>> validateParameter(Class<T> clazz,
+                                                   Constructor<T> constructor,
+                                                   Object parameterValue,
+                                                   int parameterIndex,
+                                                   Class<?>... groups);
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidatorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidatorImpl.java
new file mode 100644
index 0000000..06b5b2f
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidatorImpl.java
@@ -0,0 +1,309 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ValidationException;
+import javax.validation.metadata.ConstraintDescriptor;
+
+import org.apache.bval.jsr303.ApacheFactoryContext;
+import org.apache.bval.jsr303.BeanDescriptorImpl;
+import org.apache.bval.jsr303.ClassValidator;
+import org.apache.bval.jsr303.ConstraintValidation;
+import org.apache.bval.jsr303.ConstraintValidationListener;
+import org.apache.bval.jsr303.GroupValidationContext;
+import org.apache.bval.jsr303.groups.Group;
+import org.apache.bval.jsr303.groups.Groups;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.util.ValidationHelper;
+
+/**
+ * Description: experimental implementation of method-level-validation <br/>
+ */
+class MethodValidatorImpl extends ClassValidator implements MethodValidator {
+    /**
+     * Create a new MethodValidatorImpl instance.
+     * 
+     * @param factoryContext
+     */
+    public MethodValidatorImpl(ApacheFactoryContext factoryContext) {
+        super(factoryContext);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected BeanDescriptorImpl createBeanDescriptor(MetaBean metaBean) {
+        MethodBeanDescriptorImpl descriptor = new MethodBeanDescriptorImpl(factoryContext, metaBean);
+        MethodValidatorMetaBeanFactory factory = new MethodValidatorMetaBeanFactory(factoryContext);
+        factory.buildMethodDescriptor(descriptor);
+        return descriptor;
+    }
+
+    /**
+     * {@inheritDoc} enhancement: method-level-validation not yet completly
+     * implemented
+     * 
+     * <pre>
+     * example:
+     * <code>
+     * public @NotNull String saveItem(@Valid @NotNull Item item, @Max(23) BigDecimal
+     * </code>
+     * </pre>
+     * 
+     * spec: The constraints declarations evaluated are the constraints hosted
+     * on the parameters of the method or constructor. If @Valid is placed on a
+     * parameter, constraints declared on the object itself are considered.
+     * 
+     * @throws IllegalArgumentException
+     *             enhancement: if the method does not belong to <code>T</code>
+     *             or if the Object[] does not match the method signature
+     */
+    public <T> Set<ConstraintViolation<T>> validateParameters(Class<T> clazz, Method method, Object[] parameters,
+        Class<?>... groupArray) {
+        MethodBeanDescriptorImpl beanDesc = (MethodBeanDescriptorImpl) getConstraintsForClass(clazz);
+        MethodDescriptorImpl methodDescriptor = (MethodDescriptorImpl) beanDesc.getConstraintsForMethod(method);
+        if (methodDescriptor == null) {
+            throw new ValidationException("Method " + method + " doesn't belong to class " + clazz);
+        }
+        return validateParameters(methodDescriptor.getMetaBean(), methodDescriptor.getParameterDescriptors(),
+            parameters, groupArray);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> Set<ConstraintViolation<T>> validateParameter(Class<T> clazz, Method method, Object parameter,
+        int parameterIndex, Class<?>... groupArray) {
+        MethodBeanDescriptorImpl beanDesc = (MethodBeanDescriptorImpl) getConstraintsForClass(clazz);
+        MethodDescriptorImpl methodDescriptor = (MethodDescriptorImpl) beanDesc.getConstraintsForMethod(method);
+        if (methodDescriptor == null) {
+            throw new ValidationException("Method " + method + " doesn't belong to class " + clazz);
+        }
+        ParameterDescriptorImpl paramDesc =
+            (ParameterDescriptorImpl) methodDescriptor.getParameterDescriptors().get(parameterIndex);
+        return validateParameter(paramDesc, parameter, groupArray);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> Set<ConstraintViolation<T>> validateParameters(Class<T> clazz, Constructor<T> constructor,
+        Object[] parameters, Class<?>... groupArray) {
+        MethodBeanDescriptorImpl beanDesc = (MethodBeanDescriptorImpl) getConstraintsForClass(clazz);
+        ConstructorDescriptorImpl constructorDescriptor =
+            (ConstructorDescriptorImpl) beanDesc.getConstraintsForConstructor(constructor);
+        if (constructorDescriptor == null) {
+            throw new ValidationException("Constructor " + constructor + " doesn't belong to class " + clazz);
+        }
+        return validateParameters(constructorDescriptor.getMetaBean(), constructorDescriptor.getParameterDescriptors(),
+            parameters, groupArray);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> Set<ConstraintViolation<T>> validateParameter(Class<T> clazz, Constructor<T> constructor,
+        Object parameter, int parameterIndex, Class<?>... groupArray) {
+        MethodBeanDescriptorImpl beanDesc = (MethodBeanDescriptorImpl) getConstraintsForClass(clazz);
+        ConstructorDescriptorImpl constructorDescriptor =
+            (ConstructorDescriptorImpl) beanDesc.getConstraintsForConstructor(constructor);
+        if (constructorDescriptor == null) {
+            throw new ValidationException("Constructor " + constructor + " doesn't belong to class " + clazz);
+        }
+        ParameterDescriptorImpl paramDesc =
+            (ParameterDescriptorImpl) constructorDescriptor.getParameterDescriptors().get(parameterIndex);
+        return validateParameter(paramDesc, parameter, groupArray);
+    }
+
+    /**
+     * {@inheritDoc} If @Valid is placed on the method, the constraints declared
+     * on the object itself are considered.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> Set<ConstraintViolation<T>> validateReturnedValue(Class<T> clazz, Method method, Object returnedValue,
+        Class<?>... groupArray) {
+        MethodBeanDescriptorImpl beanDesc = (MethodBeanDescriptorImpl) getConstraintsForClass(clazz);
+        MethodDescriptorImpl methodDescriptor = (MethodDescriptorImpl) beanDesc.getConstraintsForMethod(method);
+        if (methodDescriptor == null) {
+            throw new ValidationException("Method " + method + " doesn't belong to class " + clazz);
+        }
+        final GroupValidationContext<Object> context =
+            createContext(methodDescriptor.getMetaBean(), returnedValue, null, groupArray);
+        validateReturnedValueInContext(context, methodDescriptor);
+        ConstraintValidationListener<T> result = (ConstraintValidationListener<T>) context.getListener();
+        return result.getConstraintViolations();
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> Set<ConstraintViolation<T>> validateParameters(MetaBean metaBean,
+        List<ParameterDescriptor> paramDescriptors, Object[] parameters, Class<?>... groupArray) {
+        if (parameters == null)
+            throw new IllegalArgumentException("cannot validate null");
+        if (parameters.length > 0) {
+            try {
+                GroupValidationContext<ConstraintValidationListener<Object[]>> context =
+                    createContext(metaBean, null, null, groupArray);
+                for (int i = 0; i < parameters.length; i++) {
+                    ParameterDescriptorImpl paramDesc = (ParameterDescriptorImpl) paramDescriptors.get(i);
+                    context.setBean(parameters[i]);
+                    validateParameterInContext(context, paramDesc);
+                }
+                ConstraintValidationListener<T> result = (ConstraintValidationListener<T>) context.getListener();
+                return result.getConstraintViolations();
+            } catch (RuntimeException ex) {
+                throw unrecoverableValidationError(ex, parameters);
+            }
+        } else {
+            return Collections.<ConstraintViolation<T>> emptySet();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T> Set<ConstraintViolation<T>> validateParameter(ParameterDescriptorImpl paramDesc, Object parameter,
+        Class<?>... groupArray) {
+        try {
+            final GroupValidationContext<Object> context =
+                createContext(paramDesc.getMetaBean(), parameter, null, groupArray);
+            final ConstraintValidationListener<T> result = (ConstraintValidationListener<T>) context.getListener();
+            validateParameterInContext(context, paramDesc);
+            return result.getConstraintViolations();
+        } catch (RuntimeException ex) {
+            throw unrecoverableValidationError(ex, parameter);
+        }
+    }
+
+    /**
+     * validate constraints hosted on parameters of a method
+     */
+    private <T> void validateParameterInContext(GroupValidationContext<T> context, ParameterDescriptorImpl paramDesc) {
+
+        final Groups groups = context.getGroups();
+
+        for (ConstraintDescriptor<?> consDesc : paramDesc.getConstraintDescriptors()) {
+            ConstraintValidation<?> validation = (ConstraintValidation<?>) consDesc;
+            // 1. process groups
+            for (Group current : groups.getGroups()) {
+                context.setCurrentGroup(current);
+                validation.validate(context);
+            }
+            // 2. process sequences
+            for (List<Group> eachSeq : groups.getSequences()) {
+                for (Group current : eachSeq) {
+                    context.setCurrentGroup(current);
+                    validation.validate(context);
+                    /**
+                     * if one of the group process in the sequence leads to one
+                     * or more validation failure, the groups following in the
+                     * sequence must not be processed
+                     */
+                    if (!context.getListener().isEmpty())
+                        break;
+                }
+            }
+        }
+        if (paramDesc.isCascaded() && context.getValidatedValue() != null) {
+            context
+                .setMetaBean(factoryContext.getMetaBeanFinder().findForClass(context.getValidatedValue().getClass()));
+            // 1. process groups
+            for (Group current : groups.getGroups()) {
+                context.setCurrentGroup(current);
+                ValidationHelper
+                    .validateContext(context, new Jsr303ValidationCallback(context), isTreatMapsLikeBeans());
+            }
+            // 2. process sequences
+            for (List<Group> eachSeq : groups.getSequences()) {
+                for (Group current : eachSeq) {
+                    context.setCurrentGroup(current);
+                    ValidationHelper.validateContext(context, new Jsr303ValidationCallback(context),
+                        isTreatMapsLikeBeans());
+                    /**
+                     * if one of the group process in the sequence leads to one
+                     * or more validation failure, the groups following in the
+                     * sequence must not be processed
+                     */
+                    if (!context.getListener().isEmpty())
+                        break;
+                }
+            }
+        }
+    }
+
+    /**
+     * validate constraints hosted on parameters of a method
+     */
+    private <T> void validateReturnedValueInContext(GroupValidationContext<T> context,
+        MethodDescriptorImpl methodDescriptor) {
+
+        final Groups groups = context.getGroups();
+
+        for (ConstraintDescriptor<?> consDesc : methodDescriptor.getConstraintDescriptors()) {
+            ConstraintValidation<?> validation = (ConstraintValidation<?>) consDesc;
+            // 1. process groups
+            for (Group current : groups.getGroups()) {
+                context.setCurrentGroup(current);
+                validation.validate(context);
+            }
+            // 2. process sequences
+            for (List<Group> eachSeq : groups.getSequences()) {
+                for (Group current : eachSeq) {
+                    context.setCurrentGroup(current);
+                    validation.validate(context);
+                    /**
+                     * if one of the group process in the sequence leads to one
+                     * or more validation failure, the groups following in the
+                     * sequence must not be processed
+                     */
+                    if (!context.getListener().isEmpty())
+                        break;
+                }
+            }
+        }
+        if (methodDescriptor.isCascaded() && context.getValidatedValue() != null) {
+            context
+                .setMetaBean(factoryContext.getMetaBeanFinder().findForClass(context.getValidatedValue().getClass()));
+            // 1. process groups
+            for (Group current : groups.getGroups()) {
+                context.setCurrentGroup(current);
+                ValidationHelper
+                    .validateContext(context, new Jsr303ValidationCallback(context), isTreatMapsLikeBeans());
+            }
+            // 2. process sequences
+            for (List<Group> eachSeq : groups.getSequences()) {
+                for (Group current : eachSeq) {
+                    context.setCurrentGroup(current);
+                    ValidationHelper.validateContext(context, new Jsr303ValidationCallback(context),
+                        isTreatMapsLikeBeans());
+                    /**
+                     * if one of the group process in the sequence leads to one
+                     * or more validation failure, the groups following in the
+                     * sequence must not be processed
+                     */
+                    if (!context.getListener().isEmpty())
+                        break;
+                }
+            }
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidatorMetaBeanFactory.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidatorMetaBeanFactory.java
new file mode 100644
index 0000000..6fbb2b3
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/MethodValidatorMetaBeanFactory.java
@@ -0,0 +1,175 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+import org.apache.bval.Validate;
+import org.apache.bval.jsr303.ApacheFactoryContext;
+import org.apache.bval.jsr303.AppendValidation;
+import org.apache.bval.jsr303.ConstraintAnnotationAttributes;
+import org.apache.bval.jsr303.Jsr303MetaBeanFactory;
+import org.apache.bval.model.Validation;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.commons.lang3.ClassUtils;
+
+import javax.validation.Constraint;
+import javax.validation.Valid;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+/**
+ * Description: extension to validate parameters/return values of
+ * methods/constructors.<br/>
+ */
+// TODO RSt - move. this is an optional module: move the whole package. core
+// code has no dependencies on it
+public class MethodValidatorMetaBeanFactory extends Jsr303MetaBeanFactory {
+    /**
+     * Create a new MethodValidatorMetaBeanFactory instance.
+     * 
+     * @param factoryContext
+     */
+    public MethodValidatorMetaBeanFactory(ApacheFactoryContext factoryContext) {
+        super(factoryContext);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected boolean hasValidationConstraintsDefined(Method method) {
+        return false;
+    }
+
+    /**
+     * Finish building the specified {@link MethodBeanDescriptorImpl}.
+     * 
+     * @param descriptor
+     */
+    public void buildMethodDescriptor(MethodBeanDescriptorImpl descriptor) {
+        try {
+            buildMethodConstraints(descriptor);
+            buildConstructorConstraints(descriptor);
+        } catch (Exception e) {
+            throw new IllegalArgumentException(e.getMessage(), e);
+        }
+    }
+
+    private void buildConstructorConstraints(MethodBeanDescriptorImpl beanDesc) throws InvocationTargetException,
+        IllegalAccessException {
+        beanDesc.setConstructorConstraints(new HashMap<Constructor<?>, ConstructorDescriptor>());
+
+        for (Constructor<?> cons : beanDesc.getMetaBean().getBeanClass().getDeclaredConstructors()) {
+            if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(cons)) {
+
+                ConstructorDescriptorImpl consDesc =
+                    new ConstructorDescriptorImpl(beanDesc.getMetaBean(), new Validation[0]);
+                beanDesc.putConstructorDescriptor(cons, consDesc);
+
+                Annotation[][] paramsAnnos = cons.getParameterAnnotations();
+                int idx = 0;
+                for (Annotation[] paramAnnos : paramsAnnos) {
+                    ParameterAccess access = new ParameterAccess(cons.getParameterTypes()[idx], idx);
+                    processAnnotations(consDesc, paramAnnos, access, idx);
+                    idx++;
+                }
+            }
+        }
+    }
+
+    private void buildMethodConstraints(MethodBeanDescriptorImpl beanDesc) throws InvocationTargetException,
+        IllegalAccessException {
+        beanDesc.setMethodConstraints(new HashMap<Method, MethodDescriptor>());
+
+        for (Method method : beanDesc.getMetaBean().getBeanClass().getDeclaredMethods()) {
+            if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(method)) {
+
+                MethodDescriptorImpl methodDesc = new MethodDescriptorImpl(beanDesc.getMetaBean(), new Validation[0]);
+                beanDesc.putMethodDescriptor(method, methodDesc);
+
+                // return value validations
+                AppendValidationToList validations = new AppendValidationToList();
+                ReturnAccess returnAccess = new ReturnAccess(method.getReturnType());
+                for (Annotation anno : method.getAnnotations()) {
+                    if (anno instanceof Valid || anno instanceof Validate) {
+                        methodDesc.setCascaded(true);
+                    } else {
+                        processAnnotation(anno, methodDesc, returnAccess, validations);
+                    }
+                }
+                methodDesc.addValidations(validations.getValidations());
+
+                // parameter validations
+                Annotation[][] paramsAnnos = method.getParameterAnnotations();
+                int idx = 0;
+                for (Annotation[] paramAnnos : paramsAnnos) {
+                    ParameterAccess access = new ParameterAccess(method.getParameterTypes()[idx], idx);
+                    processAnnotations(methodDesc, paramAnnos, access, idx);
+                    idx++;
+                }
+            }
+        }
+    }
+
+    private void processAnnotations(ProcedureDescriptor methodDesc, Annotation[] paramAnnos, AccessStrategy access,
+        int idx) throws InvocationTargetException, IllegalAccessException {
+        AppendValidationToList validations = new AppendValidationToList();
+        boolean cascaded = false;
+        for (Annotation anno : paramAnnos) {
+            if (anno instanceof Valid || anno instanceof Validate) {
+                cascaded = true;
+            } else {
+                processAnnotation(anno, methodDesc, access, validations);
+            }
+        }
+        ParameterDescriptorImpl paramDesc =
+            new ParameterDescriptorImpl(methodDesc.getMetaBean(), validations.getValidations().toArray(
+                new Validation[validations.getValidations().size()]));
+        paramDesc.setIndex(idx);
+        paramDesc.setCascaded(cascaded);
+        methodDesc.getParameterDescriptors().add(paramDesc);
+    }
+
+    private <A extends Annotation> void processAnnotation(A annotation, ProcedureDescriptor desc,
+        AccessStrategy access, AppendValidation validations) throws InvocationTargetException, IllegalAccessException {
+
+        if (annotation instanceof Valid || annotation instanceof Validate) {
+            desc.setCascaded(true);
+        } else {
+            Constraint vcAnno = annotation.annotationType().getAnnotation(Constraint.class);
+            if (vcAnno != null) {
+                annotationProcessor.processAnnotation(annotation, null,
+                    ClassUtils.primitiveToWrapper((Class<?>) access.getJavaType()), access, validations);
+            } else {
+                /**
+                 * Multi-valued constraints
+                 */
+                if (ConstraintAnnotationAttributes.VALUE.isDeclaredOn(annotation.annotationType())) {
+                    Annotation[] children = ConstraintAnnotationAttributes.VALUE.getValue(annotation);
+                    if (children != null) {
+                        for (Annotation child : children) {
+                            processAnnotation(child, desc, access, validations); // recursion
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterAccess.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterAccess.java
new file mode 100644
index 0000000..a0f5bc0
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterAccess.java
@@ -0,0 +1,80 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+import org.apache.bval.util.AccessStrategy;
+
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Type;
+
+
+/**
+ * Implementation of {@link AccessStrategy} for method parameters.
+ *
+ * @author Carlos Vara
+ */
+public class ParameterAccess extends AccessStrategy {
+
+    private Type paramType;
+    private int paramIdx;
+
+    /**
+     * Create a new ParameterAccess instance.
+     * @param paramType
+     * @param paramIdx
+     */
+    public ParameterAccess(Type paramType, int paramIdx ) {
+        this.paramType = paramType;
+        this.paramIdx = paramIdx;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Object get(Object instance) {
+        throw new UnsupportedOperationException("Obtaining a parameter value not yet implemented");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ElementType getElementType() {
+        return ElementType.PARAMETER;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Type getJavaType() {
+        return this.paramType;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getPropertyName() {
+        return "" + paramIdx;
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterDescriptor.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterDescriptor.java
new file mode 100644
index 0000000..97f5752
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterDescriptor.java
@@ -0,0 +1,39 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+import javax.validation.metadata.ElementDescriptor;
+
+/**
+ * Description: This class will disappear when such
+ * functionality is part of the JSR303 specification.<br/>
+ */
+public interface ParameterDescriptor extends ElementDescriptor {
+    /**
+     * Is the parameter marked by the <code>@Valid</code> annotation.
+     *
+     * @return <code>true</code> if the annotation is present, <code>false</code> otherwise.
+     */
+    boolean isCascaded();
+
+    /**
+     * Get the index of the referenced parameter.
+     * @return int
+     */
+    int getIndex();
+}
+
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterDescriptorImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterDescriptorImpl.java
new file mode 100644
index 0000000..2ca9bf0
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ParameterDescriptorImpl.java
@@ -0,0 +1,79 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+import org.apache.bval.jsr303.ElementDescriptorImpl;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.Validation;
+
+
+/**
+ * Description: {@link ParameterDescriptor} implementation.<br/>
+ */
+public class ParameterDescriptorImpl extends ElementDescriptorImpl
+      implements ParameterDescriptor {
+    private boolean cascaded;
+    private int index;
+
+    /**
+     * Create a new ParameterDescriptorImpl instance.
+     * @param metaBean
+     * @param validations
+     */
+    public ParameterDescriptorImpl(MetaBean metaBean, Validation[] validations) {
+        super(metaBean, metaBean.getClass(), validations);
+    }
+
+    /**
+     * Create a new ParameterDescriptorImpl instance.
+     * @param elementClass
+     * @param validations
+     */
+    public ParameterDescriptorImpl(Class<?> elementClass, Validation[] validations) {
+        super(elementClass, validations);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isCascaded() {
+        return cascaded;
+    }
+
+    /**
+     * Set whether the referenced parameter descriptor should be validated.
+     * @param cascaded
+     */
+    public void setCascaded(boolean cascaded) {
+        this.cascaded = cascaded;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getIndex() {
+        return index;
+    }
+
+    /**
+     * Set the index of the referenced parameter.
+     * @param index
+     */
+    public void setIndex(int index) {
+        this.index = index;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ProcedureDescriptor.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ProcedureDescriptor.java
new file mode 100644
index 0000000..a8ef674
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ProcedureDescriptor.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.bval.jsr303.extensions;
+
+
+import org.apache.bval.model.MetaBean;
+
+import java.util.List;
+
+/**
+ * Description: superinterface of {@link ConstructorDescriptor} and {@link MethodDescriptor}.<br/>
+ */
+public interface ProcedureDescriptor {
+    /**
+     * Get the owning metabean.
+     * @return MetaBean
+     */
+    MetaBean getMetaBean();
+
+    /**
+     * Set whether this procedure should be validated.
+     * @param b
+     */
+    void setCascaded(boolean b);
+
+    /**
+     * Get the parameter descriptors of this procedure.
+     * @return {@link List} of {@link ParameterDescriptor}
+     */
+    List<ParameterDescriptor> getParameterDescriptors();
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ReturnAccess.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ReturnAccess.java
new file mode 100644
index 0000000..261b52c
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/extensions/ReturnAccess.java
@@ -0,0 +1,76 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+import org.apache.bval.util.AccessStrategy;
+
+import java.lang.annotation.ElementType;
+import java.lang.reflect.Type;
+
+/**
+ * Implementation of {@link AccessStrategy} for method return values.
+ *
+ * @author Carlos Vara
+ */
+public class ReturnAccess extends AccessStrategy {
+
+    private Type returnType;
+
+    /**
+     * Create a new ReturnAccess instance.
+     * @param returnType
+     */
+    public ReturnAccess(Type returnType) {
+        this.returnType = returnType;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Object get(Object instance) {
+        throw new UnsupportedOperationException("Obtaining a method return value not yet implemented");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ElementType getElementType() {
+        return ElementType.METHOD;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Type getJavaType() {
+        return this.returnType;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getPropertyName() {
+        return "Return value";
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/Group.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/Group.java
new file mode 100644
index 0000000..3ba48eb
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/Group.java
@@ -0,0 +1,85 @@
+/*
+ * 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.bval.jsr303.groups;
+
+import javax.validation.groups.Default;
+
+import org.apache.commons.lang3.ObjectUtils;
+
+/**
+ * Immutable object that wraps an interface representing a single group.
+ */
+public final class Group {
+    /**
+     * the Default Group
+     */
+    public static final Group DEFAULT = new Group(Default.class);
+
+    private final Class<?> group;
+
+    /**
+     * Create a new Group instance.
+     * @param group
+     */
+    public Group(Class<?> group) {
+        this.group = group;
+    }
+
+    /**
+     * Get the actual group class.
+     * @return
+     */
+    public Class<?> getGroup() {
+        return group;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return "Group{" + "group=" + group + '}';
+    }
+
+    /**
+     * Learn whether the group represented is the default group.
+     * @return boolean
+     */
+	public boolean isDefault() {
+		return Default.class.equals(group);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o instanceof Group == false) return false;
+        return ObjectUtils.equals(group, ((Group) o).group);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return (group != null ? group.hashCode() : 0);
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/Groups.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/Groups.java
new file mode 100644
index 0000000..12e2526
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/Groups.java
@@ -0,0 +1,117 @@
+/*
+ * 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.bval.jsr303.groups;
+
+
+import javax.validation.GroupDefinitionException;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Defines the order to validate groups during validation.
+ * with some inspiration from reference implementation
+ *
+ * @author Roman Stumm
+ */
+public class Groups {
+    /** The list of single groups. */
+    protected List<Group> groups = new LinkedList<Group>();
+
+    /** The list of sequences. */
+    protected List<List<Group>> sequences = new LinkedList<List<Group>>();
+
+    /**
+     * Get the Groups.
+     * @return {@link List} of {@link Group}.
+     */
+    public List<Group> getGroups() {
+        return groups;
+    }
+
+    /**
+     * Get the Group sequences.
+     * @return {@link List} of {@link List} of {@link Group}
+     */
+    public List<List<Group>> getSequences() {
+        return sequences;
+    }
+
+    /**
+     * Insert a {@link Group}.
+     * @param group to insert
+     */
+    void insertGroup(Group group) {
+        if (!groups.contains(group)) {
+            groups.add(group);
+        }
+    }
+
+    /**
+     * Insert a sequence.
+     * @param groups {@link List} of {@link Group} to insert
+     */
+    void insertSequence(List<Group> groups) {
+        if (groups == null || groups.isEmpty()) {
+            return;
+        }
+
+        if (!sequences.contains(groups)) {
+            sequences.add(groups);
+        }
+    }
+
+    /**
+     * Assert that the default group can be expanded to <code>defaultGroups</code>.
+     * @param defaultGroups
+     */
+    public void assertDefaultGroupSequenceIsExpandable(List<Group> defaultGroups) {
+        for (List<Group> groupList : sequences) {
+            int idx = groupList.indexOf(Group.DEFAULT);
+            if (idx != -1) {
+                ensureExpandable(groupList, defaultGroups, idx);
+            }
+        }
+    }
+
+    private void ensureExpandable(List<Group> groupList, List<Group> defaultGroupList,
+                                  int defaultGroupIndex) {
+        for (int i = 0; i < defaultGroupList.size(); i++) {
+            Group group = defaultGroupList.get(i);
+            if (group.isDefault()) {
+                continue; // the default group is the one we want to replace
+            }
+            int index = groupList
+                  .indexOf(group); // sequence contains group of default group sequence
+            if (index == -1) {
+                continue; // if group is not in the sequence
+            }
+
+            if ((i == 0 && index == defaultGroupIndex - 1) ||
+                  (i == defaultGroupList.size() - 1 && index == defaultGroupIndex + 1)) {
+                // if we are at the beginning or end of he defaultGroupSequence and the
+                // matches are either directly before or after we can continue,
+                // since we basically have two groups
+                continue;
+            }
+            throw new GroupDefinitionException("Unable to expand default group list" +
+                  defaultGroupList + " into sequence " + groupList);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/GroupsComputer.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/GroupsComputer.java
new file mode 100644
index 0000000..49b8803
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/groups/GroupsComputer.java
@@ -0,0 +1,143 @@
+/*
+ * 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.bval.jsr303.groups;
+
+
+import javax.validation.GroupDefinitionException;
+import javax.validation.GroupSequence;
+import javax.validation.ValidationException;
+import javax.validation.groups.Default;
+import java.util.*;
+
+/**
+ * Description: compute group order, based on the RI behavior as to guarantee
+ * compatibility with interpretations of the spec.<br/>
+ * Implementation is thread-safe.
+ */
+public class GroupsComputer {
+    /** The default group array used in case any of the validate methods is called without a group. */
+    private static final Groups DEFAULT_GROUPS;
+
+    static {
+        DEFAULT_GROUPS = new GroupsComputer().computeGroups(Arrays.asList(getDefaultGroupArray()));
+    }
+
+    /**
+     * Get the default group array.
+     * @return <code>{@link Default}.class</code> only
+     */
+    public static Class<?>[] getDefaultGroupArray() {
+        return new Class<?>[] { Default.class };
+    }
+
+    /** caching resolved groups in a thread-safe map. */
+    private final Map<Class<?>, List<Group>> resolvedSequences = Collections
+        .synchronizedMap(new HashMap<Class<?>, List<Group>>());
+
+    /**
+     * Compute groups from an array of group classes.
+     * @param groups
+     * @return {@link Groups}
+     */
+    public Groups computeGroups(Class<?>[] groups) {
+        if (groups == null) {
+            throw new IllegalArgumentException("null passed as group");
+        }
+
+        // if no groups is specified use the default
+        if (groups.length == 0) {
+            return DEFAULT_GROUPS;
+        }
+
+        return computeGroups(Arrays.asList(groups));
+    }
+
+    /**
+     * Main compute implementation.
+     * @param groups
+     * @return {@link Groups}
+     */
+    protected Groups computeGroups(Collection<Class<?>> groups) {
+        if (groups == null || groups.size() == 0) {
+            throw new IllegalArgumentException("At least one group has to be specified.");
+        }
+
+        for (Class<?> clazz : groups) {
+            if (!clazz.isInterface()) {
+                throw new ValidationException(
+                      "A group has to be an interface. " + clazz.getName() + " is not.");
+            }
+        }
+
+        Groups chain = new Groups();
+        for (Class<?> clazz : groups) {
+            GroupSequence anno = clazz.getAnnotation(GroupSequence.class);
+            if (anno == null) {
+                Group group = new Group(clazz);
+                chain.insertGroup(group);
+                insertInheritedGroups(clazz, chain);
+            } else {
+                insertSequence(clazz, anno, chain);
+            }
+        }
+
+        return chain;
+    }
+
+    private void insertInheritedGroups(Class<?> clazz, Groups chain) {
+        for (Class<?> extendedInterface : clazz.getInterfaces()) {
+            Group group = new Group(extendedInterface);
+            chain.insertGroup(group);
+            insertInheritedGroups(extendedInterface, chain);
+        }
+    }
+
+    private void insertSequence(Class<?> clazz, GroupSequence anno, Groups chain) {
+        List<Group> sequence;
+        if (resolvedSequences.containsKey(clazz)) {
+            sequence = resolvedSequences.get(clazz);
+        } else {
+            sequence = resolveSequence(clazz, anno, new HashSet<Class<?>>());
+        }
+        chain.insertSequence(sequence);
+    }
+
+    private List<Group> resolveSequence(Class<?> group, GroupSequence sequenceAnnotation,
+                                        Set<Class<?>> processedSequences) {
+        if (processedSequences.contains(group)) {
+            throw new GroupDefinitionException("Cyclic dependency in groups definition");
+        } else {
+            processedSequences.add(group);
+        }
+        List<Group> resolvedGroupSequence = new LinkedList<Group>();
+        Class<?>[] sequenceArray = sequenceAnnotation.value();
+        for (Class<?> clazz : sequenceArray) {
+            GroupSequence anno = clazz.getAnnotation(GroupSequence.class);
+            if (anno == null) {
+                resolvedGroupSequence.add(new Group(clazz)); // group part of sequence
+            } else {
+                List<Group> tmpSequence =
+                      resolveSequence(clazz, anno, processedSequences);  // recursion!
+                resolvedGroupSequence.addAll(tmpSequence);
+            }
+        }
+        resolvedSequences.put(group, resolvedGroupSequence);
+        return resolvedGroupSequence;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/CachingRelevant.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/CachingRelevant.java
new file mode 100644
index 0000000..5b9502e
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/CachingRelevant.java
@@ -0,0 +1,33 @@
+/*
+ * 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.bval.jsr303.resolver;
+
+import javax.validation.TraversableResolver;
+
+/**
+ * Description: indicator interface to let the implementation choose
+ * whether results of traversable resolver should be cached.<br/>
+ */
+public interface CachingRelevant {
+    /**
+     * Learn whether the results of the {@link TraversableResolver} should be cached.
+     * @return boolean
+     */
+    boolean needsCaching();
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/CachingTraversableResolver.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/CachingTraversableResolver.java
new file mode 100644
index 0000000..bf56680
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/CachingTraversableResolver.java
@@ -0,0 +1,192 @@
+/*
+ * 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.bval.jsr303.resolver;
+
+import javax.validation.Path;
+import javax.validation.TraversableResolver;
+import java.lang.annotation.ElementType;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Cache results of a delegated traversable resovler to optimize calls
+ * It works only for a single validate* call and should not be used if
+ * the TraversableResolver is accessed concurrently
+ * <p/>
+ * Date: 25.11.2009 <br/>
+ * Time: 13:56:18 <br/>
+ *
+ * @author Roman Stumm (based on the code of Emmanuel Bernard)
+ */
+public class CachingTraversableResolver implements TraversableResolver, CachingRelevant {
+    private TraversableResolver delegate;
+    private Map<CacheEntry, CacheEntry> cache = new HashMap<CacheEntry, CacheEntry>();
+
+    /**
+     * Convenience method to check whether caching is necessary on a given {@link TraversableResolver}.
+     * @param resolver to check
+     * @return true when a CachingTraversableResolver is to be used during validation
+     */
+    public static boolean needsCaching(TraversableResolver resolver) {
+        // caching, if we do not know exactly
+        return !(resolver instanceof CachingRelevant) ||
+              ((CachingRelevant) resolver).needsCaching();
+    }
+
+    /**
+     * Create a new CachingTraversableResolver instance.
+     * @param delegate
+     */
+    public CachingTraversableResolver(TraversableResolver delegate) {
+        this.delegate = delegate;
+    }
+
+    /**
+     * If necessary, return a caching wrapper for the specified {@link TraversableResolver}.
+     * @param traversableResolver
+     * @return {@link TraversableResolver}
+     * @see #needsCaching(TraversableResolver)
+     */
+    public static TraversableResolver cacheFor(TraversableResolver traversableResolver) {
+        if (needsCaching(traversableResolver)) {
+            return new CachingTraversableResolver(traversableResolver);
+        } else {
+            return traversableResolver;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isReachable(Object traversableObject, Path.Node traversableProperty,
+                               Class<?> rootBeanType, Path pathToTraversableObject,
+                               ElementType elementType) {
+        CacheEntry currentLH = new CacheEntry(traversableObject, traversableProperty,
+              rootBeanType, pathToTraversableObject, elementType);
+        CacheEntry cachedLH = cache.get(currentLH);
+        if (cachedLH == null) {
+            currentLH.reachable = delegate.isReachable(traversableObject, traversableProperty,
+                  rootBeanType, pathToTraversableObject, elementType);
+            cache.put(currentLH, currentLH);
+            cachedLH = currentLH;
+        } else if (cachedLH.reachable == null) {
+            cachedLH.reachable = delegate.isReachable(traversableObject, traversableProperty,
+                  rootBeanType, pathToTraversableObject, elementType);
+        }
+        return cachedLH.reachable;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isCascadable(Object traversableObject, Path.Node traversableProperty,
+                                Class<?> rootBeanType, Path pathToTraversableObject,
+                                ElementType elementType) {
+        CacheEntry currentLH = new CacheEntry(traversableObject, traversableProperty,
+              rootBeanType, pathToTraversableObject, elementType);
+        CacheEntry cachedLH = cache.get(currentLH);
+        if (cachedLH == null) {
+            currentLH.cascadable = delegate.isCascadable(traversableObject,
+                  traversableProperty, rootBeanType, pathToTraversableObject, elementType);
+            cache.put(currentLH, currentLH);
+            cachedLH = currentLH;
+        } else if (cachedLH.cascadable == null) {
+            cachedLH.cascadable = delegate.isCascadable(traversableObject, traversableProperty,
+                  rootBeanType, pathToTraversableObject, elementType);
+        }
+        return cachedLH.cascadable;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean needsCaching() {
+        return false;  // I am the cache. Do not need cache for cache
+    }
+
+    /**
+     * Entry in the cache.
+     */
+    private static class CacheEntry {
+        private final Object object;
+        private final Path.Node node;
+        private final Class<?> type;
+        private final Path path;
+        private final ElementType elementType;
+        private final int hashCode;
+
+        private Boolean reachable;
+        private Boolean cascadable;
+
+        /**
+         * Create a new CacheEntry instance.
+         * @param traversableObject
+         * @param traversableProperty
+         * @param rootBeanType
+         * @param pathToTraversableObject
+         * @param elementType
+         */
+        private CacheEntry(Object traversableObject, Path.Node traversableProperty,
+                           Class<?> rootBeanType, Path pathToTraversableObject,
+                           ElementType elementType) {
+            this.object = traversableObject;
+            this.node = traversableProperty;
+            this.type = rootBeanType;
+            this.path = pathToTraversableObject;
+            this.elementType = elementType;
+            this.hashCode = buildHashCode();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            CacheEntry that = (CacheEntry) o;
+
+            return elementType == that.elementType && path.equals(that.path) &&
+                  type.equals(that.type) &&
+                  !(object != null ? !object.equals(that.object) : that.object != null) &&
+                  node.equals(that.node);
+
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public int hashCode() {
+            return hashCode;
+        }
+
+        private int buildHashCode() {
+            int result = object != null ? object.hashCode() : 0;
+            result = 31 * result + node.hashCode();
+            result = 31 * result + type.hashCode();
+            result = 31 * result + path.hashCode();
+            result = 31 * result + elementType.hashCode();
+            return result;
+        }
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/DefaultTraversableResolver.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/DefaultTraversableResolver.java
new file mode 100644
index 0000000..e6fc000
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/DefaultTraversableResolver.java
@@ -0,0 +1,123 @@
+/*
+ * 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.bval.jsr303.resolver;
+
+import java.lang.annotation.ElementType;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.validation.Path;
+import javax.validation.TraversableResolver;
+
+import org.apache.bval.jsr303.util.ClassHelper;
+import org.apache.bval.util.PrivilegedActions;
+import org.apache.commons.lang3.ClassUtils;
+
+/** @see javax.validation.TraversableResolver */
+public class DefaultTraversableResolver implements TraversableResolver, CachingRelevant {
+    private static final Logger log = Logger.getLogger(DefaultTraversableResolver.class.getName());
+
+    /** Class to load to check whether JPA 2 is on the classpath. */
+    private static final String PERSISTENCE_UTIL_CLASSNAME =
+          "javax.persistence.PersistenceUtil";
+
+    /** Class to instantiate in case JPA 2 is on the classpath. */
+    private static final String JPA_AWARE_TRAVERSABLE_RESOLVER_CLASSNAME =
+          "org.apache.bval.jsr303.resolver.JPATraversableResolver";
+
+
+    private TraversableResolver jpaTR;
+
+    /**
+     * Create a new DefaultTraversableResolver instance.
+     */
+    public DefaultTraversableResolver() {
+        initJpa();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isReachable(Object traversableObject, Path.Node traversableProperty,
+                               Class<?> rootBeanType, Path pathToTraversableObject,
+                               ElementType elementType) {
+        return jpaTR == null || jpaTR.isReachable(traversableObject, traversableProperty,
+              rootBeanType, pathToTraversableObject, elementType);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isCascadable(Object traversableObject, Path.Node traversableProperty,
+                                Class<?> rootBeanType, Path pathToTraversableObject,
+                                ElementType elementType) {
+        return jpaTR == null || jpaTR.isCascadable(traversableObject, traversableProperty,
+              rootBeanType, pathToTraversableObject, elementType);
+    }
+
+    /** Tries to load detect and load JPA. */
+    @SuppressWarnings("unchecked")
+    private void initJpa() {
+        final ClassLoader classLoader = getClassLoader();
+        try {
+            PrivilegedActions.getClass(classLoader, PERSISTENCE_UTIL_CLASSNAME);
+            log.log(Level.FINEST, String.format("Found %s on classpath.", PERSISTENCE_UTIL_CLASSNAME));
+        } catch (Exception e) {
+            log.log(Level.FINEST, String.format("Cannot find %s on classpath. All properties will per default be traversable.", PERSISTENCE_UTIL_CLASSNAME));
+            return;
+        }
+
+        try {
+            Class<? extends TraversableResolver> jpaAwareResolverClass =
+              (Class<? extends TraversableResolver>)
+                ClassUtils.getClass(classLoader, JPA_AWARE_TRAVERSABLE_RESOLVER_CLASSNAME, true);
+            jpaTR = jpaAwareResolverClass.newInstance();
+            log.log(Level.FINEST, String.format("Instantiated an instance of %s.", JPA_AWARE_TRAVERSABLE_RESOLVER_CLASSNAME));
+        } catch (Exception e) {
+			log.log(Level.WARNING,
+					String.format(
+							"Unable to load or instantiate JPA aware resolver %s. All properties will per default be traversable.",
+							JPA_AWARE_TRAVERSABLE_RESOLVER_CLASSNAME, e));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean needsCaching() {
+        return jpaTR != null && CachingTraversableResolver.needsCaching(jpaTR);
+    }
+
+    private static ClassLoader getClassLoader()
+    {
+      return (System.getSecurityManager() == null)
+        ? getClassLoader0()
+        : AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+              public ClassLoader run() {
+                return getClassLoader0();
+              }
+          });
+    }
+
+    private static ClassLoader getClassLoader0()
+    {
+      final ClassLoader loader = Thread.currentThread().getContextClassLoader();
+      return (loader != null) ? loader : ClassHelper.class.getClassLoader();
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/JPATraversableResolver.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/JPATraversableResolver.java
new file mode 100644
index 0000000..409ac52
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/JPATraversableResolver.java
@@ -0,0 +1,53 @@
+/*
+ * 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.bval.jsr303.resolver;
+
+import javax.persistence.Persistence;
+import javax.validation.Path;
+import javax.validation.TraversableResolver;
+import java.lang.annotation.ElementType;
+
+
+/** @see javax.validation.TraversableResolver */
+public class JPATraversableResolver implements TraversableResolver, CachingRelevant {
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isReachable(Object traversableObject, Path.Node traversableProperty,
+                               Class<?> rootBeanType, Path pathToTraversableObject,
+                               ElementType elementType) {
+        return traversableObject == null || Persistence.getPersistenceUtil()
+              .isLoaded(traversableObject, traversableProperty.getName());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isCascadable(Object traversableObject, Path.Node traversableProperty,
+                                Class<?> rootBeanType, Path pathToTraversableObject,
+                                ElementType elementType) {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean needsCaching() {
+        return true; // yes
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/SimpleTraversableResolver.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/SimpleTraversableResolver.java
new file mode 100644
index 0000000..ff5a3a3
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/resolver/SimpleTraversableResolver.java
@@ -0,0 +1,56 @@
+/*
+ * 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.bval.jsr303.resolver;
+
+import javax.validation.Path;
+import javax.validation.TraversableResolver;
+import java.lang.annotation.ElementType;
+
+/**
+ * Description: traversable resolver that does always resolve.<br/>
+ */
+public class SimpleTraversableResolver implements TraversableResolver, CachingRelevant {
+    /**
+     * {@inheritDoc}
+     *  @return <code>true</code>
+     */
+    public boolean isReachable(Object traversableObject, Path.Node traversableProperty,
+                               Class<?> rootBeanType, Path pathToTraversableObject,
+                               java.lang.annotation.ElementType elementType) {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return <code>true</code>
+     */
+    public boolean isCascadable(Object traversableObject, Path.Node traversableProperty,
+                                Class<?> rootBeanType, Path pathToTraversableObject,
+                                ElementType elementType) {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return <code>false</code>
+     */
+    public boolean needsCaching() {
+        return false;  // no
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ClassHelper.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ClassHelper.java
new file mode 100644
index 0000000..eeaf301
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ClassHelper.java
@@ -0,0 +1,82 @@
+/*
+ * 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.bval.jsr303.util;
+
+import org.apache.commons.lang3.ClassUtils;
+
+import java.security.AccessController;
+import java.util.List;
+
+/**
+ * Common operations on classes that do not require an {@link AccessController}.
+ * 
+ * @author Carlos Vara
+ */
+public class ClassHelper {
+
+    private ClassHelper() {
+        // No instances please
+    }
+
+    /**
+     * Fill the list with the full class/interface hierarchy of the given class.
+     * List is ordered from the most to less specific.
+     * 
+     * @param allClasses
+     *            The current list of classes in the hierarchy.
+     * @param clazz
+     *            The current class, root of the hierarchy to traverse.
+     */
+    static public void fillFullClassHierarchyAsList(List<Class<?>> allClasses, Class<?> clazz) {
+        if (clazz == null || clazz == Object.class) {
+            return;
+        }
+        if (allClasses.contains(clazz)) {
+            return;
+        }
+        allClasses.add(clazz);
+        fillFullClassHierarchyAsList(allClasses, clazz.getSuperclass());
+        for (Class<?> subClass : clazz.getInterfaces()) {
+            fillFullClassHierarchyAsList(allClasses, subClass);
+        }
+    }
+
+    /**
+     * @deprecated Will be removed for security reasons.
+     *
+     * Perform ClassUtils.getClass functions with Java 2 Security enabled.
+     */
+    @Deprecated
+    public static Class<?> getClass(String className) throws ClassNotFoundException {
+        return getClass(className, true);
+    }
+
+    /**
+     * @deprecated Will be removed for security reasons.
+     *
+     * Perform ClassUtils.getClass functions with Java 2 Security enabled.
+     */
+    @Deprecated
+    public static Class<?> getClass(String className, boolean initialize) throws ClassNotFoundException {
+        ClassLoader loader = Thread.currentThread().getContextClassLoader();
+        if (loader == null)
+          loader = ClassHelper.class.getClassLoader();
+        return ClassUtils.getClass(loader, className, initialize);
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ConstraintDefinitionValidator.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ConstraintDefinitionValidator.java
new file mode 100644
index 0000000..4b7b48b
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ConstraintDefinitionValidator.java
@@ -0,0 +1,79 @@
+/*
+ * 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.bval.jsr303.util;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintDefinitionException;
+
+import org.apache.bval.jsr303.ConstraintAnnotationAttributes;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+
+/**
+ * Internal validator that ensures the correct definition of constraint
+ * annotations.
+ * 
+ * @author Carlos Vara
+ */
+public class ConstraintDefinitionValidator {
+
+    /**
+     * Ensures that the constraint definition is valid.
+     * 
+     * @param annotation
+     *            An annotation which is annotated with {@link Constraint}.
+     * @throws ConstraintDefinitionException
+     *             In case the constraint is invalid.
+     */
+    public static void validateConstraintDefinition(Annotation annotation) {
+        ConstraintAnnotationAttributes.GROUPS.validateOn(annotation.annotationType());
+        ConstraintAnnotationAttributes.PAYLOAD.validateOn(annotation.annotationType());
+        ConstraintAnnotationAttributes.MESSAGE.validateOn(annotation.annotationType());
+        validAttributes(annotation);
+    }
+
+    /**
+     * Check that the annotation has no methods that start with "valid".
+     * 
+     * @param annotation
+     *            The annotation to check.
+     */
+    private static void validAttributes(final Annotation annotation) {
+        final Method[] methods = run(SecureActions.getDeclaredMethods(annotation.annotationType()));
+        for (Method method : methods ){
+            // Currently case insensitive, the spec is unclear about this
+            if (method.getName().toLowerCase(Locale.ENGLISH).startsWith("valid")) {
+                throw new ConstraintDefinitionException(
+                    "A constraint annotation cannot have methods which start with 'valid'");
+            }
+        }
+    }
+
+    private static <T> T run(PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/EnumerationConverter.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/EnumerationConverter.java
new file mode 100644
index 0000000..3e1394f
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/EnumerationConverter.java
@@ -0,0 +1,75 @@
+/*
+ * 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.bval.jsr303.util;
+
+import org.apache.commons.beanutils.Converter;
+
+/**
+ * A {@code org.apache.commons.beanutils.Converter} implementation to handle
+ * Enumeration type.
+ *
+ * $Id$
+ */
+public final class EnumerationConverter implements Converter {
+
+    /**
+     * The static converter instance.
+     */
+    private static final EnumerationConverter INSTANCE = new EnumerationConverter();
+
+    /**
+     * Returns this converter instance.
+     *
+     * @return this converter instance.
+     */
+    public static EnumerationConverter getInstance() {
+        return INSTANCE;
+    }
+
+    /**
+     * This class can't be instantiated.
+     */
+    private EnumerationConverter() {
+        // do nothing
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public Object convert(Class type, Object value) {
+        if (!type.isEnum()) {
+            throw new RuntimeException("Only enum types supported in this version!");
+        }
+
+        if (value == null) {
+            throw new RuntimeException("Null values not supported in this version!");
+        }
+
+        if (String.class != value.getClass()) {
+            throw new RuntimeException("Only java.lang.String values supported in this version!");
+        }
+
+        String stringValue = (String) value;
+
+        final Class<Enum> enumClass = (Class<Enum>) type;
+        return Enum.valueOf(enumClass, stringValue);
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/IOUtils.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/IOUtils.java
new file mode 100644
index 0000000..ae0653b
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/IOUtils.java
@@ -0,0 +1,53 @@
+/*
+ * 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.bval.jsr303.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Commons IO utilities.
+ *
+ * $Id$
+ */
+public final class IOUtils {
+
+    /**
+     * This class can't be instantiated
+     */
+    private IOUtils() {
+        // do nothing
+    }
+
+    /**
+     * Close quietly a {@link Closeable} reference.
+     *
+     * @param closeable the {@link Closeable} has to be closed.
+     */
+    public static void closeQuietly(Closeable closeable) {
+        if (closeable != null) {
+            try {
+                closeable.close();
+            } catch (IOException e) {
+                // do nothing
+            }
+        }
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeBuilderCustomizableContextImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeBuilderCustomizableContextImpl.java
new file mode 100644
index 0000000..114104b
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeBuilderCustomizableContextImpl.java
@@ -0,0 +1,80 @@
+/*
+ * 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.bval.jsr303.util;
+
+
+import org.apache.bval.jsr303.ConstraintValidatorContextImpl;
+
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Description: implementation of {@link NodeBuilderCustomizableContext}.<br/>
+ */
+final class NodeBuilderCustomizableContextImpl
+      implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext {
+    private final ConstraintValidatorContextImpl parent;
+    private final String messageTemplate;
+    private final PathImpl propertyPath;
+    private NodeImpl node;
+
+    /**
+     * Create a new NodeBuilderCustomizableContextImpl instance.
+     * @param contextImpl
+     * @param template
+     * @param path
+     * @param name
+     */
+    NodeBuilderCustomizableContextImpl(ConstraintValidatorContextImpl contextImpl, String template,
+                              PathImpl path, String name) {
+        parent = contextImpl;
+        messageTemplate = template;
+        propertyPath = path;
+        node = new NodeImpl(name);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder inIterable() {
+        // Modifies the "previous" node in the path
+        node.setInIterable(true);
+        return new NodeContextBuilderImpl(parent, messageTemplate, propertyPath, node);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(
+          String name) {
+        propertyPath.addNode(node);
+        node = new NodeImpl(name);
+        return this; // Re-use this instance
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintValidatorContext addConstraintViolation() {
+        propertyPath.addNode(node);
+        node = null;
+        parent.addError(messageTemplate, propertyPath);
+        return parent;
+    }
+    
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeBuilderDefinedContextImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeBuilderDefinedContextImpl.java
new file mode 100644
index 0000000..eb666c2
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeBuilderDefinedContextImpl.java
@@ -0,0 +1,64 @@
+/*
+ * 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.bval.jsr303.util;
+
+
+import org.apache.bval.jsr303.ConstraintValidatorContextImpl;
+
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Description: Implementation of {@link NodeBuilderDefinedContext}.<br/>
+ */
+public final class NodeBuilderDefinedContextImpl
+      implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext {
+    private final ConstraintValidatorContextImpl parent;
+    private final String messageTemplate;
+    private final PathImpl propertyPath;
+
+    /**
+     * Create a new NodeBuilderDefinedContextImpl instance.
+     * @param contextImpl
+     * @param template
+     * @param path
+     */
+    public NodeBuilderDefinedContextImpl(ConstraintValidatorContextImpl contextImpl, String template,
+                    PathImpl path) {
+        parent = contextImpl;
+        messageTemplate = template;
+        propertyPath = path;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(
+          String name) {
+        // Node not yet added, wait until more information is provided
+        return new NodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath, name);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintValidatorContext addConstraintViolation() {
+        parent.addError(messageTemplate, propertyPath);
+        return parent;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeContextBuilderImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeContextBuilderImpl.java
new file mode 100644
index 0000000..d6ab6ae
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeContextBuilderImpl.java
@@ -0,0 +1,91 @@
+/*
+ * 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.bval.jsr303.util;
+
+
+import org.apache.bval.jsr303.ConstraintValidatorContextImpl;
+
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder;
+
+/**
+ * Description: Implementation of {@link NodeContextBuilder}.<br/>
+ */
+final class NodeContextBuilderImpl
+      implements ConstraintValidatorContext.ConstraintViolationBuilder.NodeContextBuilder {
+    private final ConstraintValidatorContextImpl parent;
+    private final String messageTemplate;
+    private final PathImpl propertyPath;
+    // The name of the last "added" node, it will only be added if it has a non-null name
+    // The actual incorporation in the path will take place when the definition of the current leaf node is complete
+    private final NodeImpl node;
+
+    /**
+     * Create a new NodeContextBuilderImpl instance.
+     * @param contextImpl
+     * @param template
+     * @param path
+     */
+    NodeContextBuilderImpl(ConstraintValidatorContextImpl contextImpl,
+                                    String template, PathImpl path, NodeImpl node) {
+        parent = contextImpl;
+        messageTemplate = template;
+        propertyPath = path;
+        this.node = node;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext atKey(
+          Object key) {
+        node.setKey(key);
+        propertyPath.addNode(node);
+        return new NodeBuilderDefinedContextImpl(parent, messageTemplate, propertyPath);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderDefinedContext atIndex(
+          Integer index) {
+        node.setIndex(index);
+        propertyPath.addNode(node);
+        return new NodeBuilderDefinedContextImpl(parent, messageTemplate, propertyPath);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintValidatorContext.ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(
+          String name) {
+        propertyPath.addNode(node);
+        return new NodeBuilderCustomizableContextImpl(parent, messageTemplate, propertyPath, name);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ConstraintValidatorContext addConstraintViolation() {
+        propertyPath.addNode(node);
+        parent.addError(messageTemplate, propertyPath);
+        return parent;
+    }
+    
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeImpl.java
new file mode 100644
index 0000000..1d851ae
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/NodeImpl.java
@@ -0,0 +1,223 @@
+/*
+ * 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.bval.jsr303.util;
+
+import javax.validation.Path;
+import javax.validation.Path.Node;
+
+import java.io.Serializable;
+import java.util.Map;
+
+/**
+ * Description: a node (property) as part of a Path.
+ * (Implementation based on reference implementation) <br/>
+ */
+public final class NodeImpl implements Path.Node, Serializable {
+
+    private static final long serialVersionUID = 1L;
+    private static final String INDEX_OPEN = "[";
+    private static final String INDEX_CLOSE = "]";
+
+    /**
+     * Append a Node to the specified StringBuilder.
+     * @param node
+     * @param to
+     * @return to
+     */
+    public static StringBuilder appendNode(Node node, StringBuilder to) {
+        if (node.isInIterable()) {
+            to.append(INDEX_OPEN);
+            if (node.getIndex() != null) {
+                to.append(node.getIndex());
+            } else if (node.getKey() != null) {
+                to.append(node.getKey());
+            }
+            to.append(INDEX_CLOSE);
+        }
+        if (node.getName() != null) {
+            if (to.length() > 0) {
+                to.append(PathImpl.PROPERTY_PATH_SEPARATOR);
+            }
+            to.append(node.getName());
+        }
+        return to;
+    }
+
+    /**
+     * Get a NodeImpl indexed from the preceding node (or root).
+     * @param index
+     * @return NodeImpl
+     */
+    public static NodeImpl atIndex(Integer index) {
+        NodeImpl result = new NodeImpl();
+        result.setIndex(index);
+        return result;
+    }
+
+    /**
+     * Get a NodeImpl keyed from the preceding node (or root).
+     * @param key
+     * @return NodeImpl
+     */
+    public static NodeImpl atKey(Object key) {
+        NodeImpl result = new NodeImpl();
+        result.setKey(key);
+        return result;
+    }
+
+    private String name;
+    private boolean inIterable;
+    private Integer index;
+    private Object key;
+
+    /**
+     * Create a new NodeImpl instance.
+     * @param name
+     */
+    public NodeImpl(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Create a new NodeImpl instance.
+     * @param node
+     */
+    NodeImpl(Path.Node node) {
+        this.name = node.getName();
+        this.inIterable = node.isInIterable();
+        this.index = node.getIndex();
+        this.key = node.getKey();
+    }
+
+    private NodeImpl() {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * @param name the name to set
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isInIterable() {
+        return inIterable;
+    }
+
+    /**
+     * Set whether this node represents a contained value of an {@link Iterable} or {@link Map}.
+     * @param inIterable
+     */
+    public void setInIterable(boolean inIterable) {
+        this.inIterable = inIterable;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Integer getIndex() {
+        return index;
+    }
+
+    /**
+     * Set the index of this node, implying <code>inIterable</code>.
+     * @param index
+     */
+    public void setIndex(Integer index) {
+        inIterable = true;
+        this.index = index;
+        this.key = null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object getKey() {
+        return key;
+    }
+
+    /**
+     * Set the map key of this node, implying <code>inIterable</code>.
+     * @param key
+     */
+    public void setKey(Object key) {
+        inIterable = true;
+        this.key = key;
+        this.index = null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return appendNode(this, new StringBuilder()).toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        NodeImpl node = (NodeImpl) o;
+
+        if (inIterable != node.inIterable) {
+            return false;
+        }
+        if (index != null ? !index.equals(node.index) : node.index != null) {
+            return false;
+        }
+        if (key != null ? !key.equals(node.key) : node.key != null) {
+            return false;
+        }
+        if (name != null ? !name.equals(node.name) : node.name != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        int result = name != null ? name.hashCode() : 0;
+        result = 31 * result + (inIterable ? 1 : 0);
+        result = 31 * result + (index != null ? index.hashCode() : 0);
+        result = 31 * result + (key != null ? key.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/PathImpl.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/PathImpl.java
new file mode 100644
index 0000000..ba829e0
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/PathImpl.java
@@ -0,0 +1,322 @@
+/*
+ * 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.bval.jsr303.util;
+
+import javax.validation.Path;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Description: object holding the property path as a list of nodes.
+ * (Implementation partially based on reference implementation)
+ * <br/>
+ * This class is not synchronized.
+ * 
+ * @version $Rev$ $Date$
+ */
+public class PathImpl implements Path, Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    static final String PROPERTY_PATH_SEPARATOR = ".";
+
+    /**
+     * Builds non-root paths from expressions.
+     */
+    private static class PathImplBuilder implements PathNavigation.Callback<PathImpl> {
+        PathImpl result = new PathImpl();
+
+        /**
+         * {@inheritDoc}
+         */
+        public void handleProperty(String name) {
+            result.addProperty(name);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void handleIndexOrKey(String value) {
+            // with no context to guide us, we can only parse ints and fall back to String keys:
+            NodeImpl node;
+            try {
+                node = NodeImpl.atIndex(Integer.parseInt(value));
+            } catch (NumberFormatException e) {
+                node = NodeImpl.atKey(value);
+            }
+            result.addNode(node);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public PathImpl result() {
+            if (result.nodeList.isEmpty()) {
+                throw new IllegalStateException();
+            }
+            return result;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void handleGenericInIterable() {
+            result.addNode(NodeImpl.atIndex(null));
+        }
+
+    }
+
+    private final List<Node> nodeList;
+
+    /**
+     * Returns a {@code Path} instance representing the path described by the given string. To create a root node the
+     * empty string should be passed. Note: This signature is to maintain pluggability with the RI impl.
+     * 
+     * @param propertyPath
+     *            the path as string representation.
+     * @return a {@code Path} instance representing the path described by the given string.
+     */
+    public static PathImpl createPathFromString(String propertyPath) {
+        if (propertyPath == null || propertyPath.length() == 0) {
+            return create(null);
+        }
+        return PathNavigation.navigateAndReturn(propertyPath, new PathImplBuilder());
+    }
+
+    /**
+     * Create a {@link PathImpl} instance representing the specified path.
+     * 
+     * @param name
+     * @return PathImpl
+     */
+    public static PathImpl create(String name) {
+        PathImpl path = new PathImpl();
+        NodeImpl node = new NodeImpl(name);
+        path.addNode(node);
+        return path;
+    }
+
+    /**
+     * Copy another Path.
+     * 
+     * @param path
+     * @return new {@link PathImpl}
+     */
+    public static PathImpl copy(Path path) {
+        return path == null ? null : new PathImpl(path);
+    }
+
+    private PathImpl(Path path) {
+        this.nodeList = new ArrayList<Node>();
+        for (Object aPath : path) {
+            nodeList.add(new NodeImpl((Node) aPath));
+        }
+    }
+
+    private PathImpl() {
+        nodeList = new ArrayList<Node>();
+    }
+
+    private PathImpl(List<Node> nodeList) {
+        this.nodeList = new ArrayList<Node>();
+        for (Node node : nodeList) {
+            this.nodeList.add(new NodeImpl(node));
+        }
+    }
+
+    /**
+     * Learn whether this {@link PathImpl} points to the root of its graph.
+     * 
+     * @return true if no child nodes
+     */
+    // our implementation stores a nameless root node.
+    public boolean isRootPath() {
+        if (nodeList.size() != 1) {
+            return false;
+        }
+        Path.Node first = nodeList.get(0);
+        return !first.isInIterable() && first.getName() == null;
+    }
+
+    /**
+     * Return a new {@link PathImpl} that represents <code>this</code> minus its leaf node (if present).
+     * 
+     * @return PathImpl
+     */
+    public PathImpl getPathWithoutLeafNode() {
+        List<Node> nodes = new ArrayList<Node>(nodeList);
+        PathImpl path = null;
+        if (nodes.size() > 1) {
+            nodes.remove(nodes.size() - 1);
+            path = new PathImpl(nodes);
+        }
+        return path;
+    }
+
+    /**
+     * Add a node to this {@link PathImpl}.
+     * 
+     * @param node
+     *            to add
+     */
+    public void addNode(Node node) {
+        if (isRootPath()) {
+            nodeList.set(0, node);
+        } else {
+            nodeList.add(node);
+        }
+    }
+
+    /**
+     * Encapsulate the node manipulations needed to add a named property to this path.
+     * 
+     * @param name
+     */
+    public void addProperty(String name) {
+        if (!nodeList.isEmpty()) {
+            NodeImpl leaf = getLeafNode();
+            if (leaf != null && leaf.isInIterable() && leaf.getName() == null) {
+                leaf.setName(name);
+                return;
+            }
+        }
+        addNode(new NodeImpl(name));
+    }
+
+    /**
+     * Trim the leaf node from this {@link PathImpl}.
+     * 
+     * @return the node removed
+     * @throws IllegalStateException
+     *             if no nodes are found
+     */
+    public Node removeLeafNode() {
+        if (isRootPath() || nodeList.size() == 0) {
+            throw new IllegalStateException("No nodes in path!");
+        }
+        try {
+            return nodeList.remove(nodeList.size() - 1);
+        } finally {
+            if (nodeList.isEmpty()) {
+                nodeList.add(new NodeImpl((String) null));
+            }
+        }
+    }
+
+    /**
+     * Get the leaf node (if any) from this {@link PathImpl}
+     * 
+     * @return {@link NodeImpl}
+     */
+    public NodeImpl getLeafNode() {
+        if (nodeList.size() == 0) {
+            return null;
+        }
+        return (NodeImpl) nodeList.get(nodeList.size() - 1);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Iterator<Path.Node> iterator() {
+        return nodeList.iterator();
+    }
+
+    /**
+     * Learn whether <code>path</code> is a parent to <code>this</code>.
+     * 
+     * @param path
+     * @return <code>true</code> if our nodes begin with nodes equal to those found in <code>path</code>
+     */
+    public boolean isSubPathOf(Path path) {
+        if (path instanceof PathImpl && ((PathImpl) path).isRootPath()) {
+            return true;
+        }
+        Iterator<Node> pathIter = path.iterator();
+        Iterator<Node> thisIter = iterator();
+        while (pathIter.hasNext()) {
+            Node pathNode = pathIter.next();
+            if (!thisIter.hasNext()) {
+                return false;
+            }
+            Node thisNode = thisIter.next();
+            if (pathNode.isInIterable()) {
+                if (!thisNode.isInIterable()) {
+                    return false;
+                }
+                if (pathNode.getIndex() != null && !pathNode.getIndex().equals(thisNode.getIndex())) {
+                    return false;
+                }
+                if (pathNode.getKey() != null && !pathNode.getKey().equals(thisNode.getKey())) {
+                    return false;
+                }
+            } else if (thisNode.isInIterable()) {
+                // in this case we have shown that the proposed parent is not
+                // indexed, and we are, thus the paths cannot match
+                return false;
+            }
+            if (pathNode.getName() == null || pathNode.getName().equals(thisNode.getName())) {
+                continue;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        for (Path.Node node : this) {
+            NodeImpl.appendNode(node, builder);
+        }
+        return builder.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        PathImpl path = (PathImpl) o;
+        return !(nodeList != null && !nodeList.equals(path.nodeList)) && !(nodeList == null && path.nodeList != null);
+
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return nodeList != null ? nodeList.hashCode() : 0;
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/PathNavigation.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/PathNavigation.java
new file mode 100644
index 0000000..711a472
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/PathNavigation.java
@@ -0,0 +1,327 @@
+/*
+ *  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.bval.jsr303.util;
+
+import java.io.StringWriter;
+import java.text.ParsePosition;
+
+import javax.validation.ValidationException;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+
+/**
+ * Defines a path navigation algorithm and a means of interacting with same.
+ * 
+ * @version $Rev: 1136233 $ $Date: 2011-06-15 17:49:27 -0500 (Wed, 15 Jun 2011) $
+ */
+public class PathNavigation {
+
+    /**
+     * Path traversal callback function interface.
+     */
+    public interface Callback<T> {
+        /**
+         * Handle a .-delimited property.
+         * 
+         * @param name
+         */
+        void handleProperty(String name);
+
+        /**
+         * Handle an index or key embedded in [].
+         * 
+         * @param value
+         */
+        void handleIndexOrKey(String value);
+
+        /**
+         * Handle contiguous [].
+         */
+        void handleGenericInIterable();
+
+        /**
+         * Return a result. Called after navigation is complete.
+         * 
+         * @return result
+         */
+        T result();
+    }
+
+    /**
+     * Callback "procedure" that always returns null.
+     */
+    public static abstract class CallbackProcedure implements Callback<Object> {
+
+        /**
+         * {@inheritDoc}
+         */
+        public final Object result() {
+            complete();
+            return null;
+        }
+
+        /**
+         * Complete this CallbackProcedure. Default implementation is noop.
+         */
+        protected void complete() {
+        }
+    }
+
+    /**
+     * Create a new PathNavigation instance.
+     */
+    private PathNavigation() {
+    }
+
+    /**
+     * Navigate a path using the specified callback, returning its result.
+     * 
+     * @param <T>
+     * @param propertyPath
+     *            , null is assumed empty/root
+     * @param callback
+     * @return T result
+     */
+    public static <T> T navigateAndReturn(CharSequence propertyPath, Callback<? extends T> callback) {
+        try {
+            parse(propertyPath == null ? "" : propertyPath, new PathPosition(callback));
+        } catch (ValidationException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            throw new ValidationException(String.format("invalid property: %s", propertyPath), ex);
+        }
+        return callback.result();
+    }
+
+    /**
+     * Navigate a path using the specified callback.
+     * 
+     * @param propertyPath
+     * @param callback
+     */
+    public static void navigate(CharSequence propertyPath, Callback<?> callback) {
+        navigateAndReturn(propertyPath, callback);
+    }
+
+    private static void parse(CharSequence path, PathPosition pos) throws Exception {
+        int len = path.length();
+        boolean sep = true;
+        while (pos.getIndex() < len) {
+            int here = pos.getIndex();
+            char c = path.charAt(here);
+            switch (c) {
+            case ']':
+                throw new IllegalStateException(String.format("Position %s: unexpected '%s'", here, c));
+            case '[':
+                handleIndex(path, pos.next());
+                break;
+            case '.':
+                if (sep) {
+                    throw new IllegalStateException(String.format("Position %s: expected property, index/key, or end of expression", here));
+                }
+                sep = true;
+                pos.next();
+                // fall through:
+            default:
+                if (!sep) {
+                    throw new IllegalStateException(String.format("Position %s: expected property path separator, index/key, or end of expression", here));
+                }
+                pos.handleProperty(parseProperty(path, pos));
+            }
+            sep = false;
+        }
+    }
+
+    private static String parseProperty(CharSequence path, PathPosition pos) throws Exception {
+        int len = path.length();
+        int start = pos.getIndex();
+        loop: while (pos.getIndex() < len) {
+            switch (path.charAt(pos.getIndex())) {
+            case '[':
+            case ']':
+            case '.':
+                break loop;
+            }
+            pos.next();
+        }
+        if (pos.getIndex() > start) {
+            return path.subSequence(start, pos.getIndex()).toString();
+        }
+        throw new IllegalStateException(String.format("Position %s: expected property", start));
+    }
+
+    /**
+     * Handles an index/key. If the text contained between [] is surrounded by a pair of " or ', it will be treated as a
+     * string which may contain Java escape sequences.
+     * 
+     * @param path
+     * @param pos
+     * @throws Exception
+     */
+    private static void handleIndex(CharSequence path, PathPosition pos) throws Exception {
+        int len = path.length();
+        int start = pos.getIndex();
+        if (start < len) {
+            char first = path.charAt(pos.getIndex());
+            if (first == '"' || first == '\'') {
+                String s = parseQuotedString(path, pos);
+                if (s != null && path.charAt(pos.getIndex()) == ']') {
+                    pos.handleIndexOrKey(s);
+                    pos.next();
+                    return;
+                }
+            }
+            // no quoted string; match ] greedily
+            while (pos.getIndex() < len) {
+                int here = pos.getIndex();
+                try {
+                    if (path.charAt(here) == ']') {
+                        if (here == start) {
+                            pos.handleGenericInIterable();
+                        } else {
+                            pos.handleIndexOrKey(path.subSequence(start, here).toString());
+                        }
+                        return;
+                    }
+                } finally {
+                    pos.next();
+                }
+            }
+        }
+        throw new IllegalStateException(String.format("Position %s: unparsable index", start));
+    }
+
+    private static String parseQuotedString(CharSequence path, PathPosition pos) throws Exception {
+        int len = path.length();
+        int start = pos.getIndex();
+        if (start < len) {
+            char quote = path.charAt(start);
+            pos.next();
+            StringWriter w = new StringWriter();
+            while (pos.getIndex() < len) {
+                int here = pos.getIndex();
+                // look for matching quote
+                if (path.charAt(here) == quote) {
+                    pos.next();
+                    return w.toString();
+                }
+                int codePoints = StringEscapeUtils.UNESCAPE_JAVA.translate(path, here, w);
+                if (codePoints == 0) {
+                    w.write(Character.toChars(Character.codePointAt(path, here)));
+                    pos.next();
+                } else {
+                    for (int i = 0; i < codePoints; i++) {
+                        pos.plus(Character.charCount(Character.codePointAt(path, pos.getIndex())));
+                    }
+                }
+            }
+            // if reached, reset due to no ending quote found
+            pos.setIndex(start);
+        }
+        return null;
+    }
+
+    /**
+     * ParsePosition/Callback
+     */
+    private static class PathPosition extends ParsePosition implements Callback<Object> {
+        final Callback<?> delegate;
+
+        /**
+         * Create a new {@link PathPosition} instance.
+         * 
+         * @param delegate
+         */
+        private PathPosition(Callback<?> delegate) {
+            super(0);
+            this.delegate = delegate;
+        }
+
+        /**
+         * Increment and return this.
+         * 
+         * @return this
+         */
+        public PathPosition next() {
+            return plus(1);
+        }
+
+        /**
+         * Increase position and return this.
+         * 
+         * @param addend
+         * @return this
+         */
+        public PathPosition plus(int addend) {
+            setIndex(getIndex() + addend);
+            return this;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void handleProperty(String name) {
+            delegate.handleProperty(name);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void handleIndexOrKey(String value) {
+            delegate.handleIndexOrKey(value);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void handleGenericInIterable() {
+            delegate.handleGenericInIterable();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Object result() {
+            return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        /*
+         * Override equals to make findbugs happy;
+         * would simply ignore but doesn't seem to be possible at the inner class level
+         * without attaching the filter to the containing class.
+         */
+        @Override
+        public boolean equals(Object obj) {
+            return super.equals(obj);
+        }
+        
+        /**
+         * {@inheritDoc}
+         */
+        /*
+         * Override hashCode to make findbugs happy in the presence of overridden #equals :P
+         */
+        @Override
+        public int hashCode() {
+            return super.hashCode();
+        }
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/SecureActions.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/SecureActions.java
new file mode 100644
index 0000000..4cf5ce4
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/SecureActions.java
@@ -0,0 +1,151 @@
+/*
+ * 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.bval.jsr303.util;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.PrivilegedAction;
+
+import javax.validation.ValidatorFactory;
+
+import org.apache.bval.jsr303.ConfigurationImpl;
+import org.apache.bval.util.PrivilegedActions;
+
+/**
+ * Description: utility methods to perform actions with AccessController or without.<br/>
+ */
+public class SecureActions extends PrivilegedActions {
+
+    /**
+     * Create a privileged action to get the context classloader of the current thread.
+     *
+     * @see Thread#getContextClassLoader()
+     */
+    public static PrivilegedAction<ClassLoader> getContextClassLoader()
+    {
+        return SecureActions.GetContextClassLoader.instance;
+    }
+
+
+    /**
+     * Create a privileged action to get the named field declared by the specified class.
+     * The result of the action will be {@code null} if there is no such field.
+     */
+    public static PrivilegedAction<Field> getDeclaredField(final Class<?> clazz, final String fieldName) {
+        return new PrivilegedAction<Field>() {
+            public Field run() {
+                try {
+                    final Field f = clazz.getDeclaredField(fieldName);
+                    setAccessibility(f);
+                    return f;
+                } catch (final NoSuchFieldException ex) {
+                    return null;
+                }
+            }
+        };
+    }
+
+
+
+    /**
+     * Create a privileged action to get all fields declared by the specified class.
+     */
+    public static PrivilegedAction<Field[]> getDeclaredFields(final Class<?> clazz) {
+        return new PrivilegedAction<Field[]>() {
+            public Field[] run() {
+                final Field[] fields = clazz.getDeclaredFields();
+                if (fields.length > 0)
+                    AccessibleObject.setAccessible(fields, true);
+                return fields;
+            }
+        };
+    }
+
+
+
+    /**
+     * Create a privileged action to get all methods declared by the specified class.
+     */
+    public static PrivilegedAction<Method[]> getDeclaredMethods(final Class<?> clazz) {
+      // XXX 2011-03-27 jw: Inconsistent behaviour.
+      // doGetDeclaredFields() is setting fields accessible, but here we don't.
+      return new PrivilegedAction<Method[]>() {
+          public Method[] run() {
+            return clazz.getDeclaredMethods();
+        }
+      };
+    }
+
+    /**
+     * Create a privileged action to get the named method declared by the specified class
+     * or by one of its ancestors.
+     * The result of the action will be {@code null} if there is no such method.
+     */
+    public static PrivilegedAction<Method> getPublicMethod(final Class<?> clazz, final String methodName) {
+      return new PrivilegedAction<Method>() {
+          public Method run() {
+              try {
+                  return clazz.getMethod(methodName, (Class[]) null);
+              } catch (final NoSuchMethodException ex) {
+                  return null;
+              }
+          }
+      };
+    }
+
+    private static void setAccessibility(Field field) {
+      // FIXME 2011-03-27 jw:
+      // - Why not simply call field.setAccessible(true)?
+      // - Fields can not be abstract.
+        if (!Modifier.isPublic(field.getModifiers()) || (
+              Modifier.isPublic(field.getModifiers()) &&
+                    Modifier.isAbstract(field.getModifiers()))) {
+            field.setAccessible(true);
+        }
+    }
+
+    /**
+     * Create a privileged action for ConfigurationImpl.buildValidatorFactory.
+     */
+    public static PrivilegedAction<ValidatorFactory> doPrivBuildValidatorFactory(final ConfigurationImpl config) {
+        return new PrivilegedAction<ValidatorFactory>() {
+            public ValidatorFactory run() {
+                return config.doPrivBuildValidatorFactory();
+            }
+        };
+    }
+
+    private static final class GetContextClassLoader extends Object implements PrivilegedAction<ClassLoader> {
+
+      static final GetContextClassLoader instance = new GetContextClassLoader();
+
+      private GetContextClassLoader()
+      {
+        super();
+      }
+
+      public final ClassLoader run() {
+          return Thread.currentThread().getContextClassLoader();
+      }
+
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ValidationContextTraversal.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ValidationContextTraversal.java
new file mode 100644
index 0000000..766139d
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/util/ValidationContextTraversal.java
@@ -0,0 +1,204 @@
+/*
+ * 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.bval.jsr303.util;
+
+import java.lang.reflect.Type;
+
+import org.apache.bval.DynamicMetaBean;
+import org.apache.bval.jsr303.Jsr303MetaBeanFactory;
+import org.apache.bval.jsr303.UnknownPropertyException;
+import org.apache.bval.jsr303.util.PathNavigation.CallbackProcedure;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.IndexedAccess;
+import org.apache.bval.util.KeyedAccess;
+import org.apache.bval.util.PropertyAccess;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.reflect.TypeUtils;
+
+/**
+ * {@link ValidationContext} traversal {@link CallbackProcedure}.
+ * 
+ * @version $Rev: 1137074 $ $Date: 2011-06-17 18:20:30 -0500 (Fri, 17 Jun 2011) $
+ */
+public class ValidationContextTraversal extends CallbackProcedure {
+    private static class NullSafePropertyAccess extends PropertyAccess {
+
+        /**
+         * Create a new NullSafePropertyAccess instance.
+         * 
+         * @param clazz
+         * @param propertyName
+         */
+        public NullSafePropertyAccess(Class<?> clazz, String propertyName) {
+            super(clazz, propertyName);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public Object get(Object bean) {
+            return bean == null ? null : super.get(bean);
+        }
+    }
+
+    private final ValidationContext<?> validationContext;
+    private Type type;
+    private Class<?> rawType;
+
+    /**
+     * Create a new {@link ValidationContextTraversal} instance.
+     * 
+     * @param validationContext
+     */
+    public ValidationContextTraversal(ValidationContext<?> validationContext) {
+        this.validationContext = validationContext;
+        init();
+    }
+
+    /**
+     * Initialize from {@link ValidationContext}.
+     */
+    public void init() {
+        this.rawType = validationContext.getMetaBean().getBeanClass();
+        this.type = this.rawType;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void handleIndexOrKey(String token) {
+        moveDownIfNecessary();
+
+        AccessStrategy access;
+        if (IndexedAccess.getJavaElementType(type) != null) {
+            try {
+                Integer index = token == null ? null : Integer.valueOf(token);
+                access = new IndexedAccess(type, index);
+                validationContext.setCurrentIndex(index);
+            } catch (NumberFormatException e) {
+                throw new UnknownPropertyException(String.format("Cannot parse %s as an array/iterable index", token),
+                    e);
+            }
+        } else if (KeyedAccess.getJavaElementType(type) != null) {
+            access = new KeyedAccess(type, token);
+            validationContext.setCurrentKey(token);
+        } else {
+            throw new UnknownPropertyException(String.format("Cannot determine index/key type for %s", type));
+        }
+        Object value = validationContext.getBean();
+        Object child = value == null ? null : access.get(value);
+        setType(child == null ? access.getJavaType() : child.getClass());
+        validationContext.setBean(child,
+            validationContext.getMetaBean().resolveMetaBean(child == null ? rawType : child));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void handleProperty(String token) {
+        moveDownIfNecessary();
+
+        MetaBean metaBean = validationContext.getMetaBean();
+
+        if (metaBean instanceof DynamicMetaBean) {
+            metaBean = metaBean.resolveMetaBean(ObjectUtils.defaultIfNull(validationContext.getBean(), rawType));
+            validationContext.setMetaBean(metaBean);
+        }
+        MetaProperty mp = metaBean.getProperty(token);
+        if (mp == null) {
+            // TODO this could indicate a property hosted on a superclass; should we shunt the context traversal down a path based on that type?
+
+            PropertyAccess access = new PropertyAccess(rawType, token);
+            if (access.isKnown()) {
+                // add heretofore unknown, but valid, property on the fly:
+                mp = Jsr303MetaBeanFactory.addMetaProperty(metaBean, access);
+            } else {
+                throw new UnknownPropertyException("unknown property '" + token + "' in " + metaBean.getId());
+            }
+        }
+        validationContext.setMetaProperty(mp);
+        setType(mp.getType());
+    }
+
+    /**
+     * If we currently have a property, navigate the context such that the property becomes the bean, in preparation for
+     * another property.
+     * 
+     * @param validationContext
+     */
+    public void moveDownIfNecessary() {
+        MetaProperty mp = validationContext.getMetaProperty();
+        if (mp != null) {
+            if (mp.getMetaBean() == null) {
+                throw new UnknownPropertyException(String.format("Property %s.%s is not cascaded", mp
+                    .getParentMetaBean().getId(), mp.getName()));
+            }
+            validationContext.moveDown(mp, new NullSafePropertyAccess(validationContext.getMetaBean().getBeanClass(),
+                mp.getName()));
+        }
+    }
+
+    /**
+     * Set the type of the expression processed thus far.
+     * 
+     * @param type
+     */
+    protected void setType(Type type) {
+        this.rawType = TypeUtils.getRawType(type, this.type);
+        this.type = type;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void handleGenericInIterable() {
+        throw new UnsupportedOperationException("Cannot navigate a ValidationContext to []");
+    }
+
+    /**
+     * @return the type
+     */
+    public Type getType() {
+        return type;
+    }
+
+    /**
+     * @return the rawType
+     */
+    public Class<?> getRawType() {
+        return rawType;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void complete() {
+        super.complete();
+        if (validationContext.getMetaProperty() != null) {
+            return;
+        }
+        if (validationContext.getMetaBean() instanceof DynamicMetaBean) {
+            validationContext.setMetaBean(validationContext.getMetaBean().resolveMetaBean(
+                ObjectUtils.defaultIfNull(validationContext.getBean(), rawType)));
+        }
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationIgnores.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationIgnores.java
new file mode 100644
index 0000000..fa508be
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationIgnores.java
@@ -0,0 +1,149 @@
+/*
+ * 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.bval.jsr303.xml;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Description: This class instantiated during the parsing of the XML configuration
+ * data and keeps track of the annotations which should be ignored.<br/>
+ */
+public final class AnnotationIgnores {
+
+    private static final Logger log = Logger.getLogger(AnnotationIgnores.class.getName());
+
+    /**
+     * Keeps track whether the 'ignore-annotations' flag is set on bean level in the
+     * xml configuration. 
+     * If 'ignore-annotations' is not specified: default = true
+     */
+    private final Map<Class<?>, Boolean> ignoreAnnotationDefaults =
+          new HashMap<Class<?>, Boolean>();
+
+    /**
+     * Keeps track of explicitly excluded members (fields and properties) for a given class.
+     * If a member appears in
+     * the list mapped to a given class 'ignore-annotations' was explicitly set to
+     * <code>true</code> in the configuration
+     * for this class.
+     */
+    private final Map<Class<?>, List<Member>> ignoreAnnotationOnMember =
+          new HashMap<Class<?>, List<Member>>();
+
+    private final Map<Class<?>, Boolean> ignoreAnnotationOnClass =
+          new HashMap<Class<?>, Boolean>();
+
+    /**
+     * Record the ignore state for a particular annotation type.
+     * @param clazz
+     * @param b, default true if null
+     */
+    public void setDefaultIgnoreAnnotation(Class<?> clazz, Boolean b) {
+        ignoreAnnotationDefaults.put(clazz, b == null || b.booleanValue());
+    }
+
+    /**
+     * Learn whether the specified annotation type should be ignored.
+     * @param clazz
+     * @return boolean
+     */
+    public boolean getDefaultIgnoreAnnotation(Class<?> clazz) {
+        return ignoreAnnotationDefaults.containsKey(clazz)
+                && ignoreAnnotationDefaults.get(clazz);
+    }
+
+    /**
+     * Ignore annotations on a particular {@link Member} of a class.
+     * @param member
+     */
+    public void setIgnoreAnnotationsOnMember(Member member) {
+        Class<?> beanClass = member.getDeclaringClass();
+        List<Member> memberList = ignoreAnnotationOnMember.get(beanClass);
+        if (memberList == null) {
+            memberList = new ArrayList<Member>();
+            ignoreAnnotationOnMember.put(beanClass, memberList);
+        }
+        memberList.add(member);
+    }
+
+    /**
+     * Learn whether annotations should be ignored on a particular {@link Member} of a class.
+     * @param member
+     * @return boolean
+     */
+    public boolean isIgnoreAnnotations(Member member) {
+        boolean ignoreAnnotation;
+        Class<?> clazz = member.getDeclaringClass();
+        List<Member> ignoreAnnotationForMembers = ignoreAnnotationOnMember.get(clazz);
+        if (ignoreAnnotationForMembers == null ||
+              !ignoreAnnotationForMembers.contains(member)) {
+            ignoreAnnotation = getDefaultIgnoreAnnotation(clazz);
+        } else {
+            ignoreAnnotation = ignoreAnnotationForMembers.contains(member);
+        }
+        if (ignoreAnnotation) {
+            logMessage(member, clazz);
+        }
+        return ignoreAnnotation;
+    }
+
+    private void logMessage(Member member, Class<?> clazz) {
+        String type;
+        if (member instanceof Field) {
+            type = "Field";
+        } else {
+            type = "Property";
+        }
+        log.log(Level.FINEST, String.format("%s level annotations are getting ignored for %s.%s", type, clazz.getName(), member.getName()));
+    }
+
+    /**
+     * Record the ignore state of a particular class. 
+     * @param clazz
+     * @param b
+     */
+    public void setIgnoreAnnotationsOnClass(Class<?> clazz, boolean b) {
+        ignoreAnnotationOnClass.put(clazz, b);
+    }
+
+    /**
+     * Learn whether annotations should be ignored for a given class.
+     * @param clazz to check
+     * @return boolean
+     */
+    public boolean isIgnoreAnnotations(Class<?> clazz) {
+        boolean ignoreAnnotation;
+        if (ignoreAnnotationOnClass.containsKey(clazz)) {
+            ignoreAnnotation = ignoreAnnotationOnClass.get(clazz);
+        } else {
+            ignoreAnnotation = getDefaultIgnoreAnnotation(clazz);
+        }
+        if (ignoreAnnotation) {
+        	log.log(Level.FINEST, String.format("Class level annotation are getting ignored for %s", clazz.getName()));
+        }
+        return ignoreAnnotation;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationProxy.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationProxy.java
new file mode 100644
index 0000000..ea95f78
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationProxy.java
@@ -0,0 +1,130 @@
+/*
+ *  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.bval.jsr303.xml;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.bval.jsr303.util.SecureActions;
+
+/**
+ * Description: <br/>
+ * InvocationHandler implementation of <code>Annotation</code> that pretends it
+ * is a "real" source code annotation.
+ * <p/>
+ */
+// TODO move this guy up to org.apache.bval.jsr303 or
+// org.apache.bval.jsr303.model
+class AnnotationProxy implements Annotation, InvocationHandler, Serializable {
+
+    /** Serialization version */
+    private static final long serialVersionUID = 1L;
+
+    private final Class<? extends Annotation> annotationType;
+    private final Map<String, Object> values;
+
+    /**
+     * Create a new AnnotationProxy instance.
+     * 
+     * @param <A>
+     * @param descriptor
+     */
+    public <A extends Annotation> AnnotationProxy(AnnotationProxyBuilder<A> descriptor) {
+        this.annotationType = descriptor.getType();
+        values = getAnnotationValues(descriptor);
+    }
+
+    private <A extends Annotation> Map<String, Object> getAnnotationValues(AnnotationProxyBuilder<A> descriptor) {
+        Map<String, Object> result = new HashMap<String, Object>();
+        int processedValuesFromDescriptor = 0;
+        final Method[] declaredMethods = doPrivileged(
+          SecureActions.getDeclaredMethods(annotationType)
+        );
+        for (Method m : declaredMethods) {
+            if (descriptor.contains(m.getName())) {
+                result.put(m.getName(), descriptor.getValue(m.getName()));
+                processedValuesFromDescriptor++;
+            } else if (m.getDefaultValue() != null) {
+                result.put(m.getName(), m.getDefaultValue());
+            } else {
+                throw new IllegalArgumentException("No value provided for " + m.getName());
+            }
+        }
+        if (processedValuesFromDescriptor != descriptor.size()) {
+            throw new RuntimeException("Trying to instanciate " + annotationType + " with unknown paramters.");
+        }
+        return result;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+        if (values.containsKey(method.getName())) {
+            return values.get(method.getName());
+        }
+        return method.invoke(this, args);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Class<? extends Annotation> annotationType() {
+        return annotationType;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        StringBuilder result = new StringBuilder();
+        result.append('@').append(annotationType().getName()).append('(');
+        boolean comma = false;
+        for (String m : getMethodsSorted()) {
+            if (comma)
+                result.append(", ");
+            result.append(m).append('=').append(values.get(m));
+            comma = true;
+        }
+        result.append(")");
+        return result.toString();
+    }
+
+    private SortedSet<String> getMethodsSorted() {
+        SortedSet<String> result = new TreeSet<String>();
+        result.addAll(values.keySet());
+        return result;
+    }
+
+
+
+    private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationProxyBuilder.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationProxyBuilder.java
new file mode 100644
index 0000000..75cbf4a
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/AnnotationProxyBuilder.java
@@ -0,0 +1,202 @@
+/*
+ *  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.bval.jsr303.xml;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+
+import org.apache.bval.jsr303.ConstraintAnnotationAttributes;
+import org.apache.bval.jsr303.util.SecureActions;
+
+/**
+ * Description: Holds the information and creates an annotation proxy during xml
+ * parsing of validation mapping constraints. <br/>
+ */
+// TODO move this guy up to org.apache.bval.jsr303 or
+// org.apache.bval.jsr303.model
+final public class AnnotationProxyBuilder<A extends Annotation> {
+    private final Class<A> type;
+    private final Map<String, Object> elements = new HashMap<String, Object>();
+
+    /**
+     * Create a new AnnotationProxyBuilder instance.
+     *
+     * @param annotationType
+     */
+    public AnnotationProxyBuilder(Class<A> annotationType) {
+        this.type = annotationType;
+    }
+
+    /**
+     * Create a new AnnotationProxyBuilder instance.
+     *
+     * @param annotationType
+     * @param elements
+     */
+    public AnnotationProxyBuilder(Class<A> annotationType, Map<String, Object> elements) {
+        this(annotationType);
+        for (Map.Entry<String, Object> entry : elements.entrySet()) {
+            this.elements.put(entry.getKey(), entry.getValue());
+        }
+    }
+
+    /**
+     * Create a builder initially configured to create an annotation equivalent
+     * to <code>annot</code>.
+     * 
+     * @param annot Annotation to be replicated.
+     */
+    @SuppressWarnings("unchecked")
+    public AnnotationProxyBuilder(A annot) {
+        this((Class<A>) annot.annotationType());
+        // Obtain the "elements" of the annotation
+        final Method[] methods = doPrivileged(SecureActions.getDeclaredMethods(annot.annotationType()));
+        for (Method m : methods) {
+            if (!m.isAccessible()) {
+                m.setAccessible(true);
+            }
+            try {
+                Object value = m.invoke(annot);
+                this.elements.put(m.getName(), value);
+            } catch (IllegalArgumentException e) {
+                // No args, so should not happen
+                throw new ValidationException("Cannot access annotation " + annot + " element: " + m.getName());
+            } catch (IllegalAccessException e) {
+                throw new ValidationException("Cannot access annotation " + annot + " element: " + m.getName());
+            } catch (InvocationTargetException e) {
+                throw new ValidationException("Cannot access annotation " + annot + " element: " + m.getName());
+            }
+        }
+    }
+
+    /**
+     * Add an element to the configuration.
+     *
+     * @param elementName
+     * @param value
+     */
+    public void putValue(String elementName, Object value) {
+        elements.put(elementName, value);
+    }
+
+    /**
+     * Get the specified element value from the current configuration.
+     *
+     * @param elementName
+     * @return Object value
+     */
+    public Object getValue(String elementName) {
+        return elements.get(elementName);
+    }
+
+    /**
+     * Learn whether a given element has been configured.
+     *
+     * @param elementName
+     * @return <code>true</code> if an <code>elementName</code> element is found
+     *         on this annotation
+     */
+    public boolean contains(String elementName) {
+        return elements.containsKey(elementName);
+    }
+
+    /**
+     * Get the number of configured elements.
+     *
+     * @return int
+     */
+    public int size() {
+        return elements.size();
+    }
+
+    /**
+     * Get the configured Annotation type.
+     *
+     * @return Class<A>
+     */
+    public Class<A> getType() {
+        return type;
+    }
+
+    /**
+     * Configure the well-known JSR303 "message" element.
+     *
+     * @param message
+     */
+    public void setMessage(String message) {
+        ConstraintAnnotationAttributes.MESSAGE.put(elements, message);
+    }
+
+    /**
+     * Configure the well-known JSR303 "groups" element.
+     *
+     * @param groups
+     */
+    public void setGroups(Class<?>[] groups) {
+        ConstraintAnnotationAttributes.GROUPS.put(elements, groups);
+    }
+
+    /**
+     * Configure the well-known JSR303 "payload" element.
+     * 
+     * @param payload
+     */
+    public void setPayload(Class<? extends Payload>[] payload) {
+        ConstraintAnnotationAttributes.PAYLOAD.put(elements, payload);
+    }
+
+    /**
+     * Create the annotation represented by this builder.
+     *
+     * @return {@link Annotation}
+     */
+    public A createAnnotation() {
+        ClassLoader classLoader = SecureActions.getClassLoader(getType());
+        @SuppressWarnings("unchecked")
+        final Class<A> proxyClass = (Class<A>) Proxy.getProxyClass(classLoader, getType());
+        final InvocationHandler handler = new AnnotationProxy(this);
+        return doPrivileged(new PrivilegedAction<A>() {
+            public A run() {
+                try {
+                    Constructor<A> constructor = proxyClass.getConstructor(InvocationHandler.class);
+                    return constructor.newInstance(handler);
+                } catch (Exception e) {
+                    throw new ValidationException("Unable to create annotation for configured constraint", e);
+                }
+            }
+        });
+    }
+
+    private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/MetaConstraint.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/MetaConstraint.java
new file mode 100644
index 0000000..d4a6285
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/MetaConstraint.java
@@ -0,0 +1,112 @@
+/*
+ *  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.bval.jsr303.xml;
+
+
+import org.apache.bval.util.AccessStrategy;
+import org.apache.bval.util.FieldAccess;
+import org.apache.bval.util.MethodAccess;
+
+import javax.validation.ValidationException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+/**
+ * Description: hold parsed information from xml to complete MetaBean later<br/>
+ */
+//TODO move this guy up to org.apache.bval.jsr303 or org.apache.bval.jsr303.model
+//to decouple ApacheValidatorFactory from xml package and allow others to consume MetaConstraint
+public class MetaConstraint<T, A extends Annotation> {
+
+    /** The member the constraint was defined on. */
+    private final Member member;
+
+    /** The class of the bean hosting this constraint. */
+    private final Class<T> beanClass;
+
+    /** constraint annotation (proxy) */
+    private final A annotation;
+
+    private final AccessStrategy accessStrategy;
+
+    /**
+     * Create a new MetaConstraint instance.
+     * @param beanClass The class in which the constraint is defined on
+     * @param member    The member on which the constraint is defined on, {@code null} if it is a class constraint}
+     * @param annotation
+     */
+    public MetaConstraint(Class<T> beanClass, Member member, A annotation) {
+        this.member = member;
+        this.beanClass = beanClass;
+        this.annotation = annotation;
+        if (member != null) {
+            accessStrategy = createAccessStrategy(member);
+            if (accessStrategy == null || accessStrategy.getPropertyName() ==
+                  null) { // can happen if method does not follow the bean convention
+                throw new ValidationException(
+                      "Annotated method does not follow the JavaBeans naming convention: " +
+                            member);
+            }
+        } else {
+            this.accessStrategy = null;
+        }
+    }
+
+    private static AccessStrategy createAccessStrategy(Member member) {
+        if (member instanceof Method) {
+            return new MethodAccess((Method) member);
+        } else if (member instanceof Field) {
+            return new FieldAccess((Field) member);
+        } else {
+            return null; // class level
+        }
+    }
+
+    /**
+     * Get the bean class of this constraint.
+     * @return Class
+     */
+    public Class<T> getBeanClass() {
+        return beanClass;
+    }
+
+    /**
+     * Get the member to which this constraint applies.
+     * @return Member
+     */
+    public Member getMember() {
+        return member;
+    }
+
+    /**
+     * Get the annotation that defines this constraint.
+     * @return Annotation
+     */
+    public A getAnnotation() {
+        return annotation;
+    }
+
+    /**
+     * Get the access strategy used for the associated property.
+     * @return {@link AccessStrategy}
+     */
+    public AccessStrategy getAccessStrategy() {
+        return accessStrategy;
+    }
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/ValidationMappingParser.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/ValidationMappingParser.java
new file mode 100644
index 0000000..69273fb
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/ValidationMappingParser.java
@@ -0,0 +1,568 @@
+/*
+ *  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.bval.jsr303.xml;
+
+
+import java.io.InputStream;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintValidator;
+import javax.validation.Payload;
+import javax.validation.ValidationException;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.ConstraintAnnotationAttributes;
+import org.apache.bval.jsr303.util.EnumerationConverter;
+import org.apache.bval.jsr303.util.IOUtils;
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.util.FieldAccess;
+import org.apache.bval.util.MethodAccess;
+import org.apache.commons.beanutils.ConvertUtils;
+import org.apache.commons.beanutils.Converter;
+import org.apache.commons.lang3.StringUtils;
+
+
+/**
+ * Uses JAXB to parse constraints.xml based on validation-mapping-1.0.xsd.<br>
+ */
+@SuppressWarnings("restriction")
+public class ValidationMappingParser {
+    //    private static final Log log = LogFactory.getLog(ValidationMappingParser.class);
+    private static final String VALIDATION_MAPPING_XSD = "META-INF/validation-mapping-1.0.xsd";
+
+    private static final Set<ConstraintAnnotationAttributes> RESERVED_PARAMS = Collections.unmodifiableSet(EnumSet.of(
+        ConstraintAnnotationAttributes.GROUPS, ConstraintAnnotationAttributes.MESSAGE,
+        ConstraintAnnotationAttributes.PAYLOAD));
+
+    private final Set<Class<?>> processedClasses;
+    private final ApacheValidatorFactory factory;
+
+    /**
+     * Create a new ValidationMappingParser instance.
+     * @param factory
+     */
+    public ValidationMappingParser(ApacheValidatorFactory factory) {
+        this.factory = factory;
+        this.processedClasses = new HashSet<Class<?>>();
+    }
+
+    /**
+     * Parse files with constraint mappings and collect information in the factory.
+     *  
+     * @param xmlStreams - one or more contraints.xml file streams to parse
+     */
+    public void processMappingConfig(Set<InputStream> xmlStreams) throws ValidationException {
+        for (InputStream xmlStream : xmlStreams) {
+            ConstraintMappingsType mapping = parseXmlMappings(xmlStream);
+
+            String defaultPackage = mapping.getDefaultPackage();
+            processConstraintDefinitions(mapping.getConstraintDefinition(), defaultPackage);
+            for (BeanType bean : mapping.getBean()) {
+                Class<?> beanClass = loadClass(bean.getClazz(), defaultPackage);
+                if (!processedClasses.add(beanClass)) {
+                    // spec: A given class must not be described more than once amongst all
+                    //  the XML mapping descriptors.
+                    throw new ValidationException(
+                          beanClass.getName() + " has already be configured in xml.");
+                }
+                factory.getAnnotationIgnores()
+                      .setDefaultIgnoreAnnotation(beanClass, bean.isIgnoreAnnotations());
+                processClassLevel(bean.getClassType(), beanClass, defaultPackage);
+                processFieldLevel(bean.getField(), beanClass, defaultPackage);
+                processPropertyLevel(bean.getGetter(), beanClass, defaultPackage);
+                processedClasses.add(beanClass);
+            }
+        }
+    }
+
+    /** @param in XML stream to parse using the validation-mapping-1.0.xsd */
+    private ConstraintMappingsType parseXmlMappings(InputStream in) {
+        ConstraintMappingsType mappings;
+        try {
+            JAXBContext jc = JAXBContext.newInstance(ConstraintMappingsType.class);
+            Unmarshaller unmarshaller = jc.createUnmarshaller();
+            unmarshaller.setSchema(getSchema());
+            StreamSource stream = new StreamSource(in);
+            JAXBElement<ConstraintMappingsType> root =
+                  unmarshaller.unmarshal(stream, ConstraintMappingsType.class);
+            mappings = root.getValue();
+        } catch (JAXBException e) {
+            throw new ValidationException("Failed to parse XML deployment descriptor file.",
+                  e);
+        } finally {
+            IOUtils.closeQuietly(in);
+        }
+        return mappings;
+    }
+
+    /** @return validation-mapping-1.0.xsd based schema */
+    private Schema getSchema() {
+        return ValidationParser.getSchema(VALIDATION_MAPPING_XSD);
+    }
+
+    private void processClassLevel(ClassType classType, Class<?> beanClass,
+                                   String defaultPackage) {
+        if (classType == null) {
+            return;
+        }
+
+        // ignore annotation
+        if (classType.isIgnoreAnnotations() != null) {
+            factory.getAnnotationIgnores()
+                  .setIgnoreAnnotationsOnClass(beanClass, classType.isIgnoreAnnotations());
+        }
+
+        // group sequence
+        Class<?>[] groupSequence =
+              createGroupSequence(classType.getGroupSequence(), defaultPackage);
+        if (groupSequence != null) {
+            factory.addDefaultSequence(beanClass, groupSequence);
+        }
+
+        // constraints
+        for (ConstraintType constraint : classType.getConstraint()) {
+            MetaConstraint<?, ?> metaConstraint =
+                  createConstraint(constraint, beanClass, null, defaultPackage);
+            factory.addMetaConstraint(beanClass, metaConstraint);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private <A extends Annotation, T> MetaConstraint<?, ?> createConstraint(
+          ConstraintType constraint, Class<T> beanClass, Member member,
+          String defaultPackage) {
+        Class<A> annotationClass =
+              (Class<A>) loadClass(constraint.getAnnotation(), defaultPackage);
+        AnnotationProxyBuilder<A> annoBuilder = new AnnotationProxyBuilder<A>(annotationClass);
+
+        if (constraint.getMessage() != null) {
+            annoBuilder.setMessage(constraint.getMessage());
+        }
+        annoBuilder.setGroups(getGroups(constraint.getGroups(), defaultPackage));
+        annoBuilder.setPayload(getPayload(constraint.getPayload(), defaultPackage));
+
+        for (ElementType elementType : constraint.getElement()) {
+            String name = elementType.getName();
+            checkValidName(name);
+            Class<?> returnType = getAnnotationParameterType(annotationClass, name);
+            Object elementValue = getElementValue(elementType, returnType, defaultPackage);
+            annoBuilder.putValue(name, elementValue);
+        }
+        return new MetaConstraint<T, A>(beanClass, member, annoBuilder.createAnnotation());
+    }
+
+    private void checkValidName(String name) {
+        for (ConstraintAnnotationAttributes attr : RESERVED_PARAMS) {
+            if (attr.getAttributeName().equals(name)) {
+                throw new ValidationException(name + " is a reserved parameter name.");
+            }
+        }
+    }
+
+    private <A extends Annotation> Class<?> getAnnotationParameterType(
+          final Class<A> annotationClass, final String name) {
+        final Method m = doPrivileged(SecureActions.getPublicMethod(annotationClass, name));
+        if (m == null) {
+            throw new ValidationException("Annotation of type " + annotationClass.getName() +
+                  " does not contain a parameter " + name + ".");
+        }
+        return m.getReturnType();
+    }
+
+    private Object getElementValue(ElementType elementType, Class<?> returnType,
+                                   String defaultPackage) {
+        removeEmptyContentElements(elementType);
+
+        boolean isArray = returnType.isArray();
+        if (!isArray) {
+            if (elementType.getContent().size() != 1) {
+                throw new ValidationException(
+                      "Attempt to specify an array where single value is expected.");
+            }
+            return getSingleValue(elementType.getContent().get(0), returnType, defaultPackage);
+        } else {
+            List<Object> values = new ArrayList<Object>();
+            for (Serializable s : elementType.getContent()) {
+                values.add(getSingleValue(s, returnType.getComponentType(), defaultPackage));
+            }
+            return values.toArray(
+                  (Object[]) Array.newInstance(returnType.getComponentType(), values.size()));
+        }
+    }
+
+    private void removeEmptyContentElements(ElementType elementType) {
+        List<Serializable> contentToDelete = new ArrayList<Serializable>();
+        for (Serializable content : elementType.getContent()) {
+            if (content instanceof String && ((String) content).matches("[\\n ].*")) {
+                contentToDelete.add(content);
+            }
+        }
+        elementType.getContent().removeAll(contentToDelete);
+    }
+
+    @SuppressWarnings("unchecked")
+    private Object getSingleValue(Serializable serializable, Class<?> returnType,
+                                  String defaultPackage) {
+
+        Object returnValue;
+        if (serializable instanceof String) {
+            String value = (String) serializable;
+            returnValue = convertToResultType(returnType, value, defaultPackage);
+        } else if (serializable instanceof JAXBElement<?> &&
+              ((JAXBElement<?>) serializable).getDeclaredType()
+                    .equals(String.class)) {
+            JAXBElement<?> elem = (JAXBElement<?>) serializable;
+            String value = (String) elem.getValue();
+            returnValue = convertToResultType(returnType, value, defaultPackage);
+        } else if (serializable instanceof JAXBElement<?> &&
+              ((JAXBElement<?>) serializable).getDeclaredType()
+                    .equals(AnnotationType.class)) {
+            JAXBElement<?> elem = (JAXBElement<?>) serializable;
+            AnnotationType annotationType = (AnnotationType) elem.getValue();
+            try {
+                Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) returnType;
+                returnValue =
+                      createAnnotation(annotationType, annotationClass, defaultPackage);
+            } catch (ClassCastException e) {
+                throw new ValidationException("Unexpected parameter value");
+            }
+        } else {
+            throw new ValidationException("Unexpected parameter value");
+        }
+        return returnValue;
+
+    }
+
+    private Object convertToResultType(Class<?> returnType, String value,
+                                       String defaultPackage) {
+        /**
+         * Class is represented by the fully qualified class name of the class.
+         * spec: Note that if the raw string is unqualified,
+         * default package is taken into account.
+         */
+        if (returnType.equals(Class.class)) {
+            value = toQualifiedClassName(value, defaultPackage);
+        }
+
+        /* Converter lookup */
+        Converter converter = ConvertUtils.lookup(returnType);
+        if (converter == null && returnType.isEnum()) {
+            converter = EnumerationConverter.getInstance();
+        }
+
+        if (converter != null) {
+            return converter.convert(returnType, value);
+        } else {
+            return converter;
+        }
+    }
+
+    private <A extends Annotation> Annotation createAnnotation(AnnotationType annotationType,
+                                                               Class<A> returnType,
+                                                               String defaultPackage) {
+        AnnotationProxyBuilder<A> metaAnnotation = new AnnotationProxyBuilder<A>(returnType);
+        for (ElementType elementType : annotationType.getElement()) {
+            String name = elementType.getName();
+            Class<?> parameterType = getAnnotationParameterType(returnType, name);
+            Object elementValue = getElementValue(elementType, parameterType, defaultPackage);
+            metaAnnotation.putValue(name, elementValue);
+        }
+        return metaAnnotation.createAnnotation();
+    }
+
+    private Class<?>[] getGroups(GroupsType groupsType, String defaultPackage) {
+        if (groupsType == null) {
+            return new Class[]{};
+        }
+
+        List<Class<?>> groupList = new ArrayList<Class<?>>();
+        for (JAXBElement<String> groupClass : groupsType.getValue()) {
+            groupList.add(loadClass(groupClass.getValue(), defaultPackage));
+        }
+        return groupList.toArray(new Class[groupList.size()]);
+    }
+
+
+    @SuppressWarnings("unchecked")
+    private Class<? extends Payload>[] getPayload(PayloadType payloadType,
+                                                  String defaultPackage) {
+        if (payloadType == null) {
+            return new Class[]{};
+        }
+
+        List<Class<? extends Payload>> payloadList = new ArrayList<Class<? extends Payload>>();
+        for (JAXBElement<String> groupClass : payloadType.getValue()) {
+            Class<?> payload = loadClass(groupClass.getValue(), defaultPackage);
+            if (!Payload.class.isAssignableFrom(payload)) {
+                throw new ValidationException("Specified payload class " + payload.getName() +
+                      " does not implement javax.validation.Payload");
+            } else {
+                payloadList.add((Class<? extends Payload>) payload);
+            }
+        }
+        return payloadList.toArray(new Class[payloadList.size()]);
+    }
+
+    private Class<?>[] createGroupSequence(GroupSequenceType groupSequenceType,
+                                               String defaultPackage) {
+        if (groupSequenceType != null) {
+            Class<?>[] groupSequence = new Class<?>[groupSequenceType.getValue().size()];
+            int i=0;
+            for (JAXBElement<String> groupName : groupSequenceType.getValue()) {
+                Class<?> group = loadClass(groupName.getValue(), defaultPackage);
+                groupSequence[i++] = group;
+            }
+            return groupSequence;
+        } else {
+            return null;
+        }
+    }
+
+    private void processFieldLevel(List<FieldType> fields, Class<?> beanClass,
+                                   String defaultPackage) {
+        List<String> fieldNames = new ArrayList<String>();
+        for (FieldType fieldType : fields) {
+            String fieldName = fieldType.getName();
+            if (fieldNames.contains(fieldName)) {
+                throw new ValidationException(fieldName +
+                      " is defined more than once in mapping xml for bean " +
+                      beanClass.getName());
+            } else {
+                fieldNames.add(fieldName);
+            }
+            final Field field = doPrivileged(SecureActions.getDeclaredField(beanClass, fieldName));
+            if (field == null) {
+                throw new ValidationException(
+                      beanClass.getName() + " does not contain the fieldType  " + fieldName);
+            }
+
+            // ignore annotations
+            boolean ignoreFieldAnnotation = fieldType.isIgnoreAnnotations() == null ? false :
+                  fieldType.isIgnoreAnnotations();
+            if (ignoreFieldAnnotation) {
+                factory.getAnnotationIgnores().setIgnoreAnnotationsOnMember(field);
+            }
+
+            // valid
+            if (fieldType.getValid() != null) {
+                factory.addValid(beanClass, new FieldAccess(field));
+            }
+
+            // constraints
+            for (ConstraintType constraintType : fieldType.getConstraint()) {
+                MetaConstraint<?, ?> constraint =
+                      createConstraint(constraintType, beanClass, field, defaultPackage);
+                factory.addMetaConstraint(beanClass, constraint);
+            }
+        }
+    }
+
+    private void processPropertyLevel(List<GetterType> getters, Class<?> beanClass,
+                                      String defaultPackage) {
+        List<String> getterNames = new ArrayList<String>();
+        for (GetterType getterType : getters) {
+            String getterName = getterType.getName();
+            if (getterNames.contains(getterName)) {
+                throw new ValidationException(getterName +
+                      " is defined more than once in mapping xml for bean " +
+                      beanClass.getName());
+            } else {
+                getterNames.add(getterName);
+            }
+            final Method method = getGetter(beanClass, getterName);
+            if (method == null) {
+                throw new ValidationException(
+                      beanClass.getName() + " does not contain the property  " + getterName);
+            }
+
+            // ignore annotations
+            boolean ignoreGetterAnnotation = getterType.isIgnoreAnnotations() == null ? false :
+                  getterType.isIgnoreAnnotations();
+            if (ignoreGetterAnnotation) {
+                factory.getAnnotationIgnores().setIgnoreAnnotationsOnMember(method);
+            }
+
+            // valid
+            if (getterType.getValid() != null) {
+                factory.addValid(beanClass, new MethodAccess(getterName, method));
+            }
+
+            // constraints
+            for (ConstraintType constraintType : getterType.getConstraint()) {
+                MetaConstraint<?, ?> metaConstraint =
+                      createConstraint(constraintType, beanClass, method, defaultPackage);
+                factory.addMetaConstraint(beanClass, metaConstraint);
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void processConstraintDefinitions(
+          List<ConstraintDefinitionType> constraintDefinitionList, String defaultPackage) {
+        for (ConstraintDefinitionType constraintDefinition : constraintDefinitionList) {
+            String annotationClassName = constraintDefinition.getAnnotation();
+
+            Class<?> clazz = loadClass(annotationClassName, defaultPackage);
+            if (!clazz.isAnnotation()) {
+                throw new ValidationException(annotationClassName + " is not an annotation");
+            }
+            Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) clazz;
+
+            ValidatedByType validatedByType = constraintDefinition.getValidatedBy();
+            List<Class<? extends ConstraintValidator<?, ?>>> classes =
+                new ArrayList<Class<? extends ConstraintValidator<?, ?>>>();
+            /*
+             If include-existing-validator is set to false,
+             ConstraintValidator defined on the constraint annotation are ignored.
+              */
+            if (validatedByType.isIncludeExistingValidators() != null &&
+                  validatedByType.isIncludeExistingValidators()) {
+                /*
+                 If set to true, the list of ConstraintValidators described in XML
+                 are concatenated to the list of ConstraintValidator described on the
+                 annotation to form a new array of ConstraintValidator evaluated.
+                 */
+                classes.addAll(findConstraintValidatorClasses(annotationClass));
+            }
+            for (JAXBElement<String> validatorClassName : validatedByType.getValue()) {
+                Class<? extends ConstraintValidator<?, ?>> validatorClass;
+                validatorClass = (Class<? extends ConstraintValidator<?, ?>>)
+                      loadClass(validatorClassName.getValue());
+
+
+                if (!ConstraintValidator.class.isAssignableFrom(validatorClass)) {
+                    throw new ValidationException(
+                          validatorClass + " is not a constraint validator class");
+                }
+
+                /*
+                Annotation based ConstraintValidator come before XML based
+                ConstraintValidator in the array. The new list is returned
+                by ConstraintDescriptor.getConstraintValidatorClasses().
+                 */
+                if (!classes.contains(validatorClass)) classes.add(validatorClass);
+            }
+            if (factory.getConstraintsCache().containsConstraintValidator(annotationClass)) {
+                throw new ValidationException("Constraint validator for " +
+                      annotationClass.getName() + " already configured.");
+            } else {
+                factory.getConstraintsCache().putConstraintValidator(annotationClass,
+                      classes.toArray(new Class[classes.size()]));
+            }
+        }
+    }
+
+    private List<Class<? extends ConstraintValidator<? extends Annotation, ?>>> findConstraintValidatorClasses(
+          Class<? extends Annotation> annotationType) {
+        List<Class<? extends ConstraintValidator<? extends Annotation, ?>>> classes =
+              new ArrayList<Class<? extends ConstraintValidator<? extends Annotation, ?>>>();
+
+        Class<? extends ConstraintValidator<?, ?>>[] validator =
+              factory.getDefaultConstraints().getValidatorClasses(annotationType);
+        if (validator != null) {
+            classes
+                  .addAll(Arrays.asList(validator));
+        } else {
+            Class<? extends ConstraintValidator<?, ?>>[] validatedBy = annotationType
+                  .getAnnotation(Constraint.class)
+                  .validatedBy();
+            classes.addAll(Arrays.asList(validatedBy));
+        }
+        return classes;
+    }
+
+    private Class<?> loadClass(String className, String defaultPackage) {
+        return loadClass(toQualifiedClassName(className, defaultPackage));
+    }
+
+    private String toQualifiedClassName(String className, String defaultPackage) {
+        if (!isQualifiedClass(className)) {
+            className = defaultPackage + "." + className;
+        }
+        return className;
+    }
+
+    private boolean isQualifiedClass(String clazz) {
+        return clazz.contains(".");
+    }
+
+
+
+    private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+
+
+
+    private static Method getGetter(final Class<?> clazz, final String propertyName) {
+        return doPrivileged(new PrivilegedAction<Method>() {
+            public Method run() {
+                try {
+                    final String p = StringUtils.capitalize(propertyName);
+                    try {
+                        return clazz.getMethod("get" + p);
+                    } catch (NoSuchMethodException e) {
+                        return clazz.getMethod("is" + p);
+                    }
+                } catch (NoSuchMethodException e) {
+                    return null;
+                }
+            }
+        });
+
+    }
+
+
+
+    private Class<?> loadClass(final String className) {
+        ClassLoader loader = doPrivileged(SecureActions.getContextClassLoader());
+        if (loader == null)
+            loader = getClass().getClassLoader();
+
+        try {
+            return Class.forName(className, true, loader);
+        } catch (ClassNotFoundException ex) {
+            throw new ValidationException("Unable to load class: " + className, ex);
+        }
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/ValidationParser.java b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/ValidationParser.java
new file mode 100644
index 0000000..83accb2
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/java/org/apache/bval/jsr303/xml/ValidationParser.java
@@ -0,0 +1,286 @@
+/*
+ * 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.bval.jsr303.xml;
+
+
+import org.apache.bval.jsr303.ConfigurationImpl;
+import org.apache.bval.jsr303.util.IOUtils;
+import org.apache.bval.jsr303.util.SecureActions;
+import org.apache.bval.util.PrivilegedActions;
+import org.xml.sax.SAXException;
+
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.MessageInterpolator;
+import javax.validation.TraversableResolver;
+import javax.validation.ValidationException;
+import javax.validation.spi.ValidationProvider;
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Enumeration;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Description: uses jaxb to parse validation.xml<br/>
+ */
+@SuppressWarnings("restriction")
+public class ValidationParser {
+    private static final String DEFAULT_VALIDATION_XML_FILE = "META-INF/validation.xml";
+    private static final String VALIDATION_CONFIGURATION_XSD =
+            "META-INF/validation-configuration-1.0.xsd";
+    private static final Logger log = Logger.getLogger(ValidationParser.class.getName());
+    protected final String validationXmlFile;
+
+    /**
+     * Create a new ValidationParser instance.
+     *
+     * @param file
+     */
+    public ValidationParser(String file) {
+        if (file == null) {
+            validationXmlFile = DEFAULT_VALIDATION_XML_FILE;
+        } else {
+            validationXmlFile = file;
+        }
+    }
+
+    /**
+     * Process the validation configuration into <code>targetConfig</code>.
+     *
+     * @param targetConfig
+     */
+    public void processValidationConfig(ConfigurationImpl targetConfig) {
+        ValidationConfigType xmlConfig = parseXmlConfig();
+        if (xmlConfig != null) {
+            applyConfig(xmlConfig, targetConfig);
+        }
+    }
+
+    private ValidationConfigType parseXmlConfig() {
+        InputStream inputStream = null;
+        try {
+            inputStream = getInputStream(validationXmlFile);
+            if (inputStream == null) {
+            	log.log(Level.FINEST, String.format("No %s found. Using annotation based configuration only.", validationXmlFile));
+                return null;
+            }
+
+            log.log(Level.FINEST, String.format("%s found.", validationXmlFile));
+
+            Schema schema = getSchema();
+            JAXBContext jc = JAXBContext.newInstance(ValidationConfigType.class);
+            Unmarshaller unmarshaller = jc.createUnmarshaller();
+            unmarshaller.setSchema(schema);
+            StreamSource stream = new StreamSource(inputStream);
+            JAXBElement<ValidationConfigType> root =
+                    unmarshaller.unmarshal(stream, ValidationConfigType.class);
+            return root.getValue();
+        } catch (JAXBException e) {
+            throw new ValidationException("Unable to parse " + validationXmlFile, e);
+        } catch (IOException e) {
+            throw new ValidationException("Unable to parse " + validationXmlFile, e);
+        } finally {
+            IOUtils.closeQuietly(inputStream);
+        }
+    }
+
+    protected InputStream getInputStream(String path) throws IOException {
+        ClassLoader loader = PrivilegedActions.getClassLoader(getClass());
+        InputStream inputStream = loader.getResourceAsStream(path);
+
+        if (inputStream != null) {
+            // spec says: If more than one META-INF/validation.xml file
+            // is found in the classpath, a ValidationException is raised.
+            Enumeration<URL> urls = loader.getResources(path);
+            if (urls.hasMoreElements()) {
+                String url = urls.nextElement().toString();
+                while (urls.hasMoreElements()) {
+                    if (!url.equals(urls.nextElement().toString())) { // complain when first duplicate found
+                        throw new ValidationException("More than one " + path + " is found in the classpath");
+                    }
+                }
+            }
+        }
+
+        return inputStream;
+    }
+
+    private Schema getSchema() {
+        return getSchema(VALIDATION_CONFIGURATION_XSD);
+    }
+
+    /**
+     * Get a Schema object from the specified resource name.
+     *
+     * @param xsd
+     * @return {@link Schema}
+     */
+    static Schema getSchema(String xsd) {
+        ClassLoader loader = PrivilegedActions.getClassLoader(ValidationParser.class);
+        SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        URL schemaUrl = loader.getResource(xsd);
+        try {
+            return sf.newSchema(schemaUrl);
+        } catch (SAXException e) {
+            log.log(Level.WARNING, String.format("Unable to parse schema: %s", xsd), e);
+            return null;
+        }
+    }
+
+    private void applyConfig(ValidationConfigType xmlConfig, ConfigurationImpl targetConfig) {
+        applyProviderClass(xmlConfig, targetConfig);
+        applyMessageInterpolator(xmlConfig, targetConfig);
+        applyTraversableResolver(xmlConfig, targetConfig);
+        applyConstraintFactory(xmlConfig, targetConfig);
+        applyMappingStreams(xmlConfig, targetConfig);
+        applyProperties(xmlConfig, targetConfig);
+    }
+
+    private void applyProperties(ValidationConfigType xmlConfig, ConfigurationImpl target) {
+        for (PropertyType property : xmlConfig.getProperty()) {
+            if (log.isLoggable(Level.FINEST)) {
+                log.log(Level.FINEST, String.format("Found property '%s' with value '%s' in %s", property.getName(), property.getValue(), validationXmlFile));
+            }
+            target.addProperty(property.getName(), property.getValue());
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void applyProviderClass(ValidationConfigType xmlConfig, ConfigurationImpl target) {
+        String providerClassName = xmlConfig.getDefaultProvider();
+        if (providerClassName != null) {
+            Class<? extends ValidationProvider<?>> clazz =
+                    (Class<? extends ValidationProvider<?>>) loadClass(providerClassName);
+            target.setProviderClass(clazz);
+            log.log(Level.INFO, String.format("Using %s as validation provider.", providerClassName));
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void applyMessageInterpolator(ValidationConfigType xmlConfig,
+                                          ConfigurationImpl target) {
+        String messageInterpolatorClass = xmlConfig.getMessageInterpolator();
+        if (target.getMessageInterpolator() == null) {
+            if (messageInterpolatorClass != null) {
+                Class<MessageInterpolator> clazz = (Class<MessageInterpolator>)
+                        loadClass(messageInterpolatorClass);
+                target.messageInterpolator(newInstance(clazz));
+                log.log(Level.INFO, String.format("Using %s as message interpolator.", messageInterpolatorClass));
+            }
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void applyTraversableResolver(ValidationConfigType xmlConfig,
+                                          ConfigurationImpl target) {
+        String traversableResolverClass = xmlConfig.getTraversableResolver();
+        if (target.getTraversableResolver() == null) {
+            if (traversableResolverClass != null) {
+                Class<TraversableResolver> clazz = (Class<TraversableResolver>)
+                        loadClass(traversableResolverClass);
+                target.traversableResolver(newInstance(clazz));
+                log.log(Level.INFO, String.format("Using %s as traversable resolver.", traversableResolverClass));
+            }
+        }
+    }
+
+    private <T> T newInstance(final Class<T> cls) {
+        return AccessController.doPrivileged(new PrivilegedAction<T>() {
+            public T run() {
+                try {
+                    return cls.newInstance();
+                } catch (final Exception ex) {
+                    throw new ValidationException("Cannot instantiate : " + cls, ex);
+                }
+            }
+        });
+    }
+
+    @SuppressWarnings("unchecked")
+    private void applyConstraintFactory(ValidationConfigType xmlConfig,
+                                        ConfigurationImpl target) {
+        String constraintFactoryClass = xmlConfig.getConstraintValidatorFactory();
+        if (target.getConstraintValidatorFactory() == null) {
+            if (constraintFactoryClass != null) {
+                Class<ConstraintValidatorFactory> clazz = (Class<ConstraintValidatorFactory>)
+                        loadClass(constraintFactoryClass);
+                target.constraintValidatorFactory(newInstance(clazz));
+                log.log(Level.INFO, String.format("Using %s as constraint factory.", constraintFactoryClass));
+            }
+        }
+    }
+
+    private void applyMappingStreams(ValidationConfigType xmlConfig,
+                                     ConfigurationImpl target) {
+        for (JAXBElement<String> mappingFileNameElement : xmlConfig.getConstraintMapping()) {
+            String mappingFileName = mappingFileNameElement.getValue();
+            if (mappingFileName.startsWith("/")) {
+                // Classloader needs a path without a starting /
+                mappingFileName = mappingFileName.substring(1);
+            }
+            log.log(Level.FINEST, String.format("Trying to open input stream for %s", mappingFileName));
+            InputStream in = null;
+            try {
+                in = getInputStream(mappingFileName);
+                if (in == null) {
+                    throw new ValidationException(
+                            "Unable to open input stream for mapping file " +
+                                    mappingFileName);
+                }
+            } catch (IOException e) {
+                throw new ValidationException("Unable to open input stream for mapping file " +
+                        mappingFileName, e);
+            }
+            target.addMapping(in);
+        }
+    }
+
+
+    private static <T> T doPrivileged(final PrivilegedAction<T> action) {
+        if (System.getSecurityManager() != null) {
+            return AccessController.doPrivileged(action);
+        } else {
+            return action.run();
+        }
+    }
+
+    private Class<?> loadClass(final String className) {
+        ClassLoader loader = doPrivileged(SecureActions.getContextClassLoader());
+        if (loader == null)
+            loader = getClass().getClassLoader();
+
+        try {
+            return Class.forName(className, true, loader);
+        } catch (ClassNotFoundException ex) {
+            throw new ValidationException("Unable to load class: " + className, ex);
+        }
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/main/resources/META-INF/services/javax.validation.spi.ValidationProvider b/trunk/bval-jsr303/src/main/resources/META-INF/services/javax.validation.spi.ValidationProvider
new file mode 100644
index 0000000..af55120
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/resources/META-INF/services/javax.validation.spi.ValidationProvider
@@ -0,0 +1,17 @@
+# 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.
+org.apache.bval.jsr303.ApacheValidationProvider
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/DefaultConstraints.properties b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/DefaultConstraints.properties
new file mode 100644
index 0000000..6ed1e94
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/DefaultConstraints.properties
@@ -0,0 +1,53 @@
+# 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.
+
+# configuration for mapping of javax.validation.constraints to ConstraintValidator implementations
+# the annotations from the JSR303 validation-api are mentioned here, but you could
+# also extend the configuration to get a default implementation for other constraint-annotations.
+# format: key=constraint-annotation-class value=constraint-validator-class (multiple classes: comma-separated)
+
+javax.validation.constraints.AssertFalse=org.apache.bval.constraints.AssertFalseValidator
+javax.validation.constraints.AssertTrue=org.apache.bval.constraints.AssertTrueValidator
+javax.validation.constraints.DecimalMax=org.apache.bval.constraints.DecimalMaxValidatorForNumber,\
+  org.apache.bval.constraints.DecimalMaxValidatorForString
+javax.validation.constraints.DecimalMin=org.apache.bval.constraints.DecimalMinValidatorForNumber,\
+  org.apache.bval.constraints.DecimalMinValidatorForString
+javax.validation.constraints.Digits=org.apache.bval.constraints.DigitsValidatorForNumber,\
+  org.apache.bval.constraints.DigitsValidatorForString
+javax.validation.constraints.Future=org.apache.bval.constraints.FutureValidatorForDate,\
+  org.apache.bval.constraints.FutureValidatorForCalendar
+javax.validation.constraints.Max=org.apache.bval.constraints.MaxValidatorForNumber,\
+  org.apache.bval.constraints.MaxValidatorForString
+javax.validation.constraints.Min=org.apache.bval.constraints.MinValidatorForNumber,\
+  org.apache.bval.constraints.MinValidatorForString
+javax.validation.constraints.NotNull=org.apache.bval.constraints.NotNullValidator
+javax.validation.constraints.Null=org.apache.bval.constraints.NullValidator
+javax.validation.constraints.Past=org.apache.bval.constraints.PastValidatorForDate,\
+  org.apache.bval.constraints.PastValidatorForCalendar
+javax.validation.constraints.Size=org.apache.bval.constraints.SizeValidatorForString,\
+  org.apache.bval.constraints.SizeValidatorForMap,\
+  org.apache.bval.constraints.SizeValidatorForCollection,\
+  org.apache.bval.constraints.SizeValidatorForArrayOfBoolean,\
+  org.apache.bval.constraints.SizeValidatorForArrayOfByte,\
+  org.apache.bval.constraints.SizeValidatorForArrayOfChar,\
+  org.apache.bval.constraints.SizeValidatorForArrayOfDouble,\
+  org.apache.bval.constraints.SizeValidatorForArrayOfFloat,\
+  org.apache.bval.constraints.SizeValidatorForArrayOfInt,\
+  org.apache.bval.constraints.SizeValidatorForArrayOfLong,\
+  org.apache.bval.constraints.SizeValidatorForArrayOfObject,\
+  org.apache.bval.constraints.SizeValidatorForArrayOfShort
+javax.validation.constraints.Pattern=org.apache.bval.constraints.PatternValidator
diff --git a/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages.properties b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages.properties
new file mode 100644
index 0000000..5e4fd97
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages.properties
@@ -0,0 +1,37 @@
+# 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.
+
+# The properties listed below are resolved by the default message resolver.
+
+# standard
+javax.validation.constraints.Null.message=must be null
+javax.validation.constraints.NotNull.message=may not be null
+javax.validation.constraints.AssertTrue.message=must be true
+javax.validation.constraints.AssertFalse.message=must be false
+javax.validation.constraints.Min.message=must be greater than or equal to {value}
+javax.validation.constraints.Max.message=must be less than or equal to {value}
+javax.validation.constraints.Size.message=size must be between {min} and {max}
+javax.validation.constraints.Digits.message=numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected)
+javax.validation.constraints.Past.message=must be a past date
+javax.validation.constraints.Future.message=must be a future date
+javax.validation.constraints.Pattern.message=must match the following regular expression: {regexp}
+javax.validation.constraints.DecimalMax.message=must be less than or equal to {value}
+javax.validation.constraints.DecimalMin.message=must be greater than or equal to {value}
+
+# additional built-ins
+org.apache.bval.constraints.NotEmpty.message=may not be empty
+org.apache.bval.constraints.Email.message=not a well-formed email address
diff --git a/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_de.properties b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_de.properties
new file mode 100644
index 0000000..d55a8fd
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_de.properties
@@ -0,0 +1,37 @@
+# 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.
+
+# The properties listed below are resolved by the default message resolver.
+
+# standard
+javax.validation.constraints.Null.message=muss "null" sein
+javax.validation.constraints.NotNull.message=darf nicht "null" sein
+javax.validation.constraints.AssertTrue.message=muss "true" sein
+javax.validation.constraints.AssertFalse.message=muss "false" sein
+javax.validation.constraints.Min.message=muss gr\u00F6\u00DFer oder gleich {value} sein
+javax.validation.constraints.Max.message=muss kleiner oder gleich {value} sein
+javax.validation.constraints.Size.message=Gr\u00F6\u00DFe muss zwischen {min} und {max} liegen
+javax.validation.constraints.Digits.message=numerischer Wert au\u00DFerhalb des G\u00FCltigkeitsbereiches (erwarte: <{integer} digits>.<{fraction} digits>)
+javax.validation.constraints.Past.message=muss Datum in der Vergangenheit sein
+javax.validation.constraints.Future.message=muss Datum in der Zukunft sein
+javax.validation.constraints.Pattern.message=Muss mit regul\u00E4rem Ausdruck \u00FCbereinstimmen: {regexp}
+javax.validation.constraints.DecimalMax.message=muss kleiner oder gleich {value} sein
+javax.validation.constraints.DecimalMin.message=muss gr\u00F6\u00DFer oder gleich {value} sein
+
+# additional built-ins
+org.apache.bval.constraints.NotEmpty.message=darf nicht leer sein
+org.apache.bval.constraints.Email.message=muss g\u00FCltiges Format einer EMail-Adresse sein
diff --git a/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_en.properties b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_en.properties
new file mode 100644
index 0000000..4ecfad3
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_en.properties
@@ -0,0 +1,20 @@
+# 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.
+
+# intentionally empty, because english messages are located in the default bundle.
+# this file ensures, that a lookup with an english locale uses the default bundle and not the
+# bundle of the default locale, which is platform specific
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_es.properties b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_es.properties
new file mode 100644
index 0000000..97e1761
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_es.properties
@@ -0,0 +1,37 @@
+# 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.
+
+# The properties listed below are resolved by the default message resolver.
+
+# standard
+javax.validation.constraints.Null.message=tiene que ser null
+javax.validation.constraints.NotNull.message=no puede ser null
+javax.validation.constraints.AssertTrue.message=tiene que ser true
+javax.validation.constraints.AssertFalse.message=tiene que ser false
+javax.validation.constraints.Min.message=tiene que ser menor o igual que {value}
+javax.validation.constraints.Max.message=tiene que ser mayor o igual que {value}
+javax.validation.constraints.Size.message=el tamaño tiene que estar comprendido entre {min} y {max}
+javax.validation.constraints.Digits.message=valor numérico fuera de los límites (se espera <{integer} cifras>.<{fraction} cifras>)
+javax.validation.constraints.Past.message=tiene que ser una fecha en el pasado
+javax.validation.constraints.Future.message=tiene que ser una fecha en el futuro
+javax.validation.constraints.Pattern.message=tiene que corresponder a la expresión regular "{regexp}"
+javax.validation.constraints.DecimalMax.message=tiene que ser mayor o igual que {value}
+javax.validation.constraints.DecimalMin.message=tiene que ser menor o igual que {value}
+
+# additional built-ins
+org.apache.bval.constraints.NotEmpty.message=no puede ser vacio
+org.apache.bval.constraints.Email.message=tiene que ser un correo email bien formado
diff --git a/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_it.properties b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_it.properties
new file mode 100644
index 0000000..87e3273
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/resources/org/apache/bval/jsr303/ValidationMessages_it.properties
@@ -0,0 +1,37 @@
+# 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.
+
+# The properties listed below are resolved by the default message resolver.
+
+# standard
+javax.validation.constraints.Null.message=deve essere null
+javax.validation.constraints.NotNull.message=non pu\u00F2 essere null
+javax.validation.constraints.AssertTrue.message=deve essere true
+javax.validation.constraints.AssertFalse.message=deve essere false
+javax.validation.constraints.Min.message=deve essere minore o uguale di {value}
+javax.validation.constraints.Max.message=deve essere maggiore o uguale di {value}
+javax.validation.constraints.Size.message=le dimensioni devono essere tra {min} e {max}
+javax.validation.constraints.Digits.message=valore numerico fuori dai limiti (atteso <{integer} cifre>.<{fraction} cifre>)
+javax.validation.constraints.Past.message=deve essere una data nel passato
+javax.validation.constraints.Future.message=deve essere una data futura
+javax.validation.constraints.Pattern.message=deve corrispondere all'espressione regolare "{regexp}"
+javax.validation.constraints.DecimalMax.message=deve essere maggiore o uguale di {value}
+javax.validation.constraints.DecimalMin.message=deve essere minore o uguale di {value}
+
+# additional built-ins
+org.apache.bval.constraints.NotEmpty.message=non pu\u00F2 essere vuoto
+org.apache.bval.constraints.Email.message=non \u00E8 un indirizzo email ben formato
diff --git a/trunk/bval-jsr303/src/main/xjb/binding-customization.xjb b/trunk/bval-jsr303/src/main/xjb/binding-customization.xjb
new file mode 100644
index 0000000..2e76014
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/xjb/binding-customization.xjb
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
+    xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc">
+    <jxb:bindings schemaLocation="../xsd/validation-mapping-1.0.xsd" node="/xs:schema">
+        <jxb:globalBindings>
+            <xjc:javaType name="java.lang.String" xmlType="xs:string" 
+                adapter="javax.xml.bind.annotation.adapters.CollapsedStringAdapter"/>
+        </jxb:globalBindings>
+
+        <jxb:bindings node="//xs:complexType[9]/xs:sequence[1]/xs:element[1]">
+            <jxb:property name="classType"/>
+        </jxb:bindings>
+    </jxb:bindings>
+</jxb:bindings>
diff --git a/trunk/bval-jsr303/src/main/xsd/validation-configuration-1.0.xsd b/trunk/bval-jsr303/src/main/xsd/validation-configuration-1.0.xsd
new file mode 100644
index 0000000..5476714
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/xsd/validation-configuration-1.0.xsd
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<xs:schema attributeFormDefault="unqualified"
+           elementFormDefault="qualified"
+           targetNamespace="http://jboss.org/xml/ns/javax/validation/configuration"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           version="1.0">
+    <xs:element name="validation-config" type="config:validation-configType"
+                xmlns:config="http://jboss.org/xml/ns/javax/validation/configuration"/>
+    <xs:complexType name="validation-configType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="default-provider" minOccurs="0"/>
+            <xs:element type="xs:string" name="message-interpolator" minOccurs="0"/>
+            <xs:element type="xs:string" name="traversable-resolver" minOccurs="0"/>
+            <xs:element type="xs:string" name="constraint-validator-factory" minOccurs="0"/>
+            <xs:element type="xs:string" name="constraint-mapping" maxOccurs="unbounded" minOccurs="0"/>
+            <xs:element type="config:propertyType" name="property" maxOccurs="unbounded" minOccurs="0"
+                        xmlns:config="http://jboss.org/xml/ns/javax/validation/configuration"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="propertyType">
+        <xs:simpleContent>
+            <xs:extension base="xs:string">
+                <xs:attribute name="name" use="required" type="xs:string"/>
+            </xs:extension>
+        </xs:simpleContent>
+    </xs:complexType>
+</xs:schema>
diff --git a/trunk/bval-jsr303/src/main/xsd/validation-mapping-1.0.xsd b/trunk/bval-jsr303/src/main/xsd/validation-mapping-1.0.xsd
new file mode 100644
index 0000000..87c34b9
--- /dev/null
+++ b/trunk/bval-jsr303/src/main/xsd/validation-mapping-1.0.xsd
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<xs:schema attributeFormDefault="unqualified"
+           elementFormDefault="qualified"
+           targetNamespace="http://jboss.org/xml/ns/javax/validation/mapping"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           version="1.0">
+    <xs:element name="constraint-mappings"
+                type="map:constraint-mappingsType"
+                xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+
+    <xs:complexType name="payloadType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="groupsType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="groupSequenceType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="constraint-mappingsType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="default-package" minOccurs="0"/>
+            <xs:element type="map:beanType"
+                        name="bean"
+                        maxOccurs="unbounded"
+                        minOccurs="0"
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+            <xs:element type="map:constraint-definitionType"
+                        name="constraint-definition"
+                        maxOccurs="unbounded"
+                        minOccurs="0"
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="validated-byType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+        </xs:sequence>
+        <xs:attribute type="xs:boolean" name="include-existing-validators" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="constraintType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="message" minOccurs="0"/>
+            <xs:element type="map:groupsType"
+                        name="groups"
+                        minOccurs="0"
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+            <xs:element type="map:payloadType"
+                        name="payload"
+                        minOccurs="0"
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>            
+            <xs:element type="map:elementType"
+                        name="element"
+                        maxOccurs="unbounded"
+                        minOccurs="0"
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="annotation" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="elementType" mixed="true">
+        <xs:sequence>
+            <xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
+            <xs:element type="map:annotationType"
+                        name="annotation"
+                        maxOccurs="unbounded"
+                        minOccurs="0"
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="name" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="classType">
+        <xs:sequence>
+            <xs:element type="map:groupSequenceType" 
+                        name="group-sequence" 
+                        minOccurs="0" 
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+            <xs:element type="map:constraintType"
+                        name="constraint"
+                        maxOccurs="unbounded"
+                        minOccurs="0"
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+        </xs:sequence>
+        <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="beanType">
+        <xs:sequence>
+            <xs:element type="map:classType"
+                        name="class"
+                        minOccurs="0"
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping">
+            </xs:element>
+            <xs:element type="map:fieldType"
+                        name="field"
+                        minOccurs="0"
+                        maxOccurs="unbounded"
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+            <xs:element type="map:getterType"
+                        name="getter"
+                        minOccurs="0"
+                        maxOccurs="unbounded"
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="class" use="required"/>
+        <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="annotationType">
+        <xs:sequence>
+            <xs:element type="map:elementType"
+                        name="element"
+                        maxOccurs="unbounded"
+                        minOccurs="0"
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="getterType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="valid" minOccurs="0" fixed=""/>
+            <xs:element type="map:constraintType"
+                        name="constraint"
+                        minOccurs="0"
+                        maxOccurs="unbounded"
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="name" use="required"/>
+        <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="constraint-definitionType">
+        <xs:sequence>
+            <xs:element type="map:validated-byType"
+                        name="validated-by"
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="annotation" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="fieldType">
+        <xs:sequence>
+            <xs:element type="xs:string" name="valid" minOccurs="0" fixed=""/>
+            <xs:element type="map:constraintType"
+                        name="constraint"
+                        minOccurs="0"
+                        maxOccurs="unbounded"
+                        xmlns:map="http://jboss.org/xml/ns/javax/validation/mapping"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="name" use="required"/>
+        <xs:attribute type="xs:boolean" name="ignore-annotations" use="optional"/>
+    </xs:complexType>
+</xs:schema>
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AssertFalseValidatorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AssertFalseValidatorTest.java
new file mode 100644
index 0000000..4677d34
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AssertFalseValidatorTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.bval.constraints;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+/**
+ * Checks correct behaviour of {@link AssertFalseValidator}.
+ * <p>
+ * Per the spec:
+ * <ul>
+ * <li>The annotated element must be false.</li>
+ * <li><code>null</code> elements are considered valid.</li>
+ * </ul>
+ * 
+ * TODO: Mock context and verify that it's not used during validation.
+ * 
+ * @see "bean_validation-1_0_CR1-pfd-spec#Chapter6#Example6.4"
+ * 
+ * @author Carlos Vara
+ */
+public class AssertFalseValidatorTest extends TestCase {
+	
+    public static Test suite() {
+        return new TestSuite(AssertFalseValidatorTest.class);
+    }
+    
+    public AssertFalseValidatorTest(String name) {
+    	super(name);
+    }
+    
+    /**
+     * Test {@link AssertFalseValidator} with <code>null</code> context.
+     */
+    public void testAssertFalseValidator() {
+    	AssertFalseValidator afv = new AssertFalseValidator();
+    	assertFalse("True value validation must fail", afv.isValid(true, null));
+    	assertTrue("False value validation must succeed", afv.isValid(false, null));
+    	assertTrue("Null value validation must succeed", afv.isValid(null, null));
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AssertTrueValidatorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AssertTrueValidatorTest.java
new file mode 100644
index 0000000..016fe02
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AssertTrueValidatorTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.bval.constraints;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+/**
+ * Checks correct behaviour of {@link AssertTrueValidator}.
+ * <p>
+ * Per the spec:
+ * <ul>
+ * <li>The annotated element must be true.</li>
+ * <li><code>null</code> elements are considered valid.</li>
+ * </ul>
+ * 
+ * TODO: Mock context and verify that it's not used during validation.
+ * 
+ * @see "bean_validation-1_0_CR1-pfd-spec#Chapter6#Example6.3"
+ * 
+ * @author Carlos Vara
+ */
+public class AssertTrueValidatorTest extends TestCase {
+	
+    public static Test suite() {
+        return new TestSuite(AssertTrueValidatorTest.class);
+    }
+    
+    public AssertTrueValidatorTest(String name) {
+    	super(name);
+    }
+    
+    /**
+     * Test {@link AssertTrueValidator} with null context.
+     */
+    public void testAssertTrueValidator() {
+    	AssertTrueValidator atv = new AssertTrueValidator();
+    	assertTrue("True value validation must succeed", atv.isValid(true, null));
+    	assertFalse("False value validation must fail", atv.isValid(false, null));
+    	assertTrue("Null value validation must succeed", atv.isValid(null, null));
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AuthorAddressKnown.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AuthorAddressKnown.java
new file mode 100644
index 0000000..4240b5d
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/AuthorAddressKnown.java
@@ -0,0 +1,75 @@
+/*
+ *  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.bval.constraints;
+
+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.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.Payload;
+
+import org.apache.bval.jsr303.example.Address;
+import org.apache.bval.jsr303.example.Author;
+
+/**
+ * 
+ * 
+ * @version $Rev: 999729 $ $Date: 2010-09-21 21:37:54 -0500 (Tue, 21 Sep 2010) $
+ */
+@Target( { ANNOTATION_TYPE, METHOD, FIELD })
+@Constraint(validatedBy = AuthorAddressKnown.Validator.class)
+@Retention(RUNTIME)
+public @interface AuthorAddressKnown {
+
+    String message() default "{org.apache.bval.constraints.AuthorAddressKnown.message}";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default { };
+
+    public static class Validator implements ConstraintValidator<AuthorAddressKnown, Author> {
+
+        /**
+         * {@inheritDoc}
+         */
+        public void initialize(AuthorAddressKnown constraintAnnotation) {
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isValid(Author value, ConstraintValidatorContext context) {
+            if (value.getAddresses() == null) {
+                return false;
+            }
+            for (Address address : value.getAddresses()) {
+                if (address != null) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/CompanyEmail.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/CompanyEmail.java
new file mode 100644
index 0000000..154cd2f
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/CompanyEmail.java
@@ -0,0 +1,67 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.OverridesAttribute;
+import javax.validation.Payload;
+import javax.validation.constraints.Pattern;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Pattern.List({
+    // email
+    @Pattern(regexp = "[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}"),
+    // agimatec
+    @Pattern(regexp = ".*?COMPANY.*?")
+})
+/**
+ * test a constraint WITHOUT an own ConstraintValidator implementation.
+ * the validations, that must be processed are in the combined constraints only!!
+ * the @Constraint annotation is nevertheless required so that the framework searches
+ * for combined constraints.  
+ */
+@Constraint(validatedBy = {})
+@Documented
+@Target({ANNOTATION_TYPE, METHOD, FIELD, CONSTRUCTOR, PARAMETER})
+@Retention(RUNTIME)
+public @interface CompanyEmail {
+    String message() default "Not an email of COMPANY";
+
+    @OverridesAttribute(constraint = Pattern.class, name = "message",
+          constraintIndex = 0) String emailMessage() default "Not an email";
+
+    @OverridesAttribute(constraint = Pattern.class, name = "message",
+          constraintIndex = 1) String agimatecMessage() default "Not COMPANY";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+
+    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
+    @Retention(RUNTIME)
+    @Documented
+          @interface List {
+        CompanyEmail[] value();
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/DecimalMinMaxValidatorsTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/DecimalMinMaxValidatorsTest.java
new file mode 100644
index 0000000..d0e0bb8
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/DecimalMinMaxValidatorsTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.bval.constraints;
+
+import junit.framework.TestCase;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.constraints.DecimalMax;
+import javax.validation.constraints.DecimalMin;
+import java.util.Set;
+
+/**
+ * Description: <br>
+ * User: roman.stumm<br>
+ * Date: 06.04.2010<br>
+ * Time: 13:45:09<br>
+ */
+public class DecimalMinMaxValidatorsTest extends TestCase {
+
+    @DecimalMin("922392239223.06")
+    public double dmin;
+    @DecimalMax("922392239223.09")
+    public double dmax;
+
+    public void testDecimalMinValue() {
+        Validator v = Validation.buildDefaultValidatorFactory().getValidator();
+
+        this.dmin = 922392239223.05;
+        this.dmax = 922392239223.08;
+
+        Set<ConstraintViolation<DecimalMinMaxValidatorsTest>> res = v.validate(this);
+        assertFalse("Min validation failed", res.isEmpty());
+    }
+
+    public void testDecimalMaxValue() {
+        Validator v = Validation.buildDefaultValidatorFactory().getValidator();
+
+        this.dmin = Double.MAX_VALUE;
+        this.dmax = 922392239223.1;
+
+        Set<ConstraintViolation<DecimalMinMaxValidatorsTest>> res = v.validate(this);
+        assertFalse("Max validation failed", res.isEmpty());
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/DigitsValidatorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/DigitsValidatorTest.java
new file mode 100644
index 0000000..745ed2b
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/DigitsValidatorTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.bval.constraints;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.math.BigDecimal;
+
+/**
+ * DigitsConstraintValidator Tester.
+ *
+ * @author <Authors name>
+ * @since <pre>02/03/2009</pre>
+ * @version 1.0
+ */
+public class DigitsValidatorTest extends TestCase {
+    public DigitsValidatorTest(String name) {
+        super(name);
+    }
+
+    public void testValidateNumber() {
+        DigitsValidatorForNumber validator = new DigitsValidatorForNumber();
+        validator.setFractional(4);
+        validator.setIntegral(2);
+        Assert.assertFalse(validator.isValid(new BigDecimal("100.1234"), null));
+        Assert.assertFalse(validator.isValid(new BigDecimal("99.12345"), null));
+        Assert.assertTrue(validator.isValid(new BigDecimal("99.1234"), null));
+        Assert.assertFalse(validator.isValid(Double.valueOf(100.1234), null));
+        Assert.assertFalse(validator.isValid(Double.valueOf(99.12345), null));
+        Assert.assertTrue(validator.isValid(Double.valueOf(99.1234), null));
+        Assert.assertTrue(validator.isValid(Double.valueOf(99.123400), null));
+        Assert.assertTrue(validator.isValid(new BigDecimal("99.123400"), null));
+    }
+
+    public void testValidateString() {
+        DigitsValidatorForString validator = new DigitsValidatorForString();
+        validator.setFractional(4);
+        validator.setIntegral(2);
+        String val = "100.12345";
+        Assert.assertFalse(validator.isValid(val, null));
+        val = "99.1234";
+        Assert.assertTrue(validator.isValid(val, null));
+    }
+
+    public void testValidateNumber2() {
+        DigitsValidatorForNumber validator = new DigitsValidatorForNumber();
+        validator.setFractional(4);
+        validator.setIntegral(2);
+        Long val = new Long("100");
+        Assert.assertFalse(validator.isValid(val, null));
+        val = new Long("99");
+        Assert.assertTrue(validator.isValid(val, null));
+    }
+
+    public void testValidateString2() {
+        DigitsValidatorForString validator = new DigitsValidatorForString();
+        validator.setFractional(0);
+        validator.setIntegral(2);
+        String val = "99.5";
+        Assert.assertFalse(validator.isValid(val, null));
+        val = "99";
+        Assert.assertTrue(validator.isValid(val, null));
+    }
+
+    public static Test suite() {
+        return new TestSuite(DigitsValidatorTest.class);
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/EmailValidatorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/EmailValidatorTest.java
new file mode 100644
index 0000000..cbae43a
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/EmailValidatorTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.bval.constraints;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.example.Customer;
+
+import javax.validation.Validator;
+
+/**
+ * EmailValidator Tester.
+ *
+ * @author Roman Stumm
+ * @version 1.0
+ * @since <pre>10/14/2008</pre>
+ */
+public class EmailValidatorTest extends TestCase {
+    public static class EmailAddressBuilder {
+        @Email
+        private StringBuilder buffer = new StringBuilder();
+
+        /**
+         * Get the buffer.
+         * @return StringBuilder
+         */
+        public StringBuilder getBuffer() {
+            return buffer;
+        }
+
+    }
+
+    private Validator validator;
+
+    public EmailValidatorTest(String name) {
+        super(name);
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        validator = ApacheValidatorFactory.getDefault().getValidator();
+    }
+
+    public void testEmail() {
+        Customer customer = new Customer();
+        customer.setCustomerId("id-1");
+        customer.setFirstName("Mary");
+        customer.setLastName("Do");
+        customer.setPassword("12345");
+
+        Assert.assertEquals(0, validator.validate(customer).size());
+
+        customer.setEmailAddress("some@invalid@address");
+        Assert.assertEquals(1, validator.validate(customer).size());
+
+        customer.setEmailAddress("some.valid-012345@address_at-test.org");
+        Assert.assertEquals(0, validator.validate(customer).size());
+    }
+
+    public void testEmailCharSequence() {
+        EmailAddressBuilder emailAddressBuilder = new EmailAddressBuilder();
+        Assert.assertEquals(0, validator.validate(emailAddressBuilder).size());
+        emailAddressBuilder.getBuffer().append("foo");
+        Assert.assertEquals(1, validator.validate(emailAddressBuilder).size());
+        emailAddressBuilder.getBuffer().append('@');
+        Assert.assertEquals(1, validator.validate(emailAddressBuilder).size());
+        emailAddressBuilder.getBuffer().append("bar");
+        Assert.assertEquals(0, validator.validate(emailAddressBuilder).size());
+        emailAddressBuilder.getBuffer().append('.');
+        Assert.assertEquals(1, validator.validate(emailAddressBuilder).size());
+        emailAddressBuilder.getBuffer().append("baz");
+        Assert.assertEquals(0, validator.validate(emailAddressBuilder).size());
+    }
+
+    public static Test suite() {
+        return new TestSuite(EmailValidatorTest.class);
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/FrenchZipCode.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/FrenchZipCode.java
new file mode 100644
index 0000000..80e5b4e
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/FrenchZipCode.java
@@ -0,0 +1,60 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.OverridesAttribute;
+import javax.validation.Payload;
+import javax.validation.ReportAsSingleViolation;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Description: example for composed constraint.
+ * not implemented! simple dummy implemenation for tests only! <br/>
+ */
+@NotEmpty
+@NotNull
+@Size(min = 4, max = 5, message = "Zipcode should be of size {value}")
+@Constraint(validatedBy = FrenchZipcodeValidator.class)
+@ReportAsSingleViolation
+@Documented
+@Target({ANNOTATION_TYPE, METHOD, FIELD})
+@Retention(RUNTIME)
+public @interface FrenchZipCode {
+    @OverridesAttribute.List({
+            @OverridesAttribute(constraint = Size.class, name= "min"),
+        @OverridesAttribute(constraint = Size.class, name = "max")})
+    int size() default 6;
+
+    @OverridesAttribute(constraint=Size.class, name="message")
+    String sizeMessage() default "{error.zipcode.size}";
+
+    String message() default "Wrong zipcode";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default { };
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/FrenchZipcodeValidator.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/FrenchZipcodeValidator.java
new file mode 100644
index 0000000..dc92142
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/FrenchZipcodeValidator.java
@@ -0,0 +1,35 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Description: not implemented (test only)<br/>
+ */
+public class FrenchZipcodeValidator implements ConstraintValidator<FrenchZipCode, Object> {
+    public void initialize(FrenchZipCode constraintAnnotation) {
+        // do nothing
+    }
+
+    public boolean isValid(Object object, ConstraintValidatorContext validationContext) {
+        return null != object;
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/HasStringValidator.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/HasStringValidator.java
new file mode 100644
index 0000000..7f2be60
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/HasStringValidator.java
@@ -0,0 +1,39 @@
+/*
+ * 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.bval.constraints;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Description: <br/>
+ */
+public class HasStringValidator implements ConstraintValidator<HasValue, String> {
+    private String[] values;
+
+    public void initialize(HasValue stringValues) {
+        values = stringValues.value();
+    }
+
+    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
+        return s == null || ArrayUtils.contains(values, s);
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/HasValue.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/HasValue.java
new file mode 100644
index 0000000..abc0125
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/HasValue.java
@@ -0,0 +1,43 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Description: allow distinct string values for element (like enums) <br/>
+ */
+@Target({ANNOTATION_TYPE, METHOD, FIELD})
+@Constraint(validatedBy = {HasStringValidator.class})
+@Retention(RUNTIME)
+public @interface HasValue {
+    String[] value();
+
+    String message() default "Wrong value, must be one of {value}";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default { };
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/MinMaxValidatorsForNumberTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/MinMaxValidatorsForNumberTest.java
new file mode 100644
index 0000000..e4f68dc
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/MinMaxValidatorsForNumberTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.bval.constraints;
+
+import junit.framework.TestCase;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import java.util.Set;
+
+/**
+ * Check correct behaviour of {@link MinValidatorForNumber} and
+ * {@link MaxValidatorForNumber} on boundary values.
+ * <p/>
+ * The chosen numbers: 9223372036854775806l and 9223372036854775807l cast to the
+ * same double value.
+ * 
+ * @author Carlos Vara
+ */
+public class MinMaxValidatorsForNumberTest extends TestCase {
+
+    @Min(value = 9223372036854775807l)
+    public long min;
+
+    @Max(value = 9223372036854775806l)
+    public long max;
+
+    public void testMinBoundaryValue() {
+        Validator v = Validation.buildDefaultValidatorFactory().getValidator();
+
+        this.min = 9223372036854775806l;
+        this.max = 0l;
+
+        // Current min value is smaller, should fail, but it doesn't
+        Set<ConstraintViolation<MinMaxValidatorsForNumberTest>> res = v.validate(this);
+        assertFalse("Min validation failed", res.isEmpty());
+    }
+
+    public void testMaxBoundaryValue() {
+        Validator v = Validation.buildDefaultValidatorFactory().getValidator();
+
+        this.min = Long.MAX_VALUE;
+        this.max = 9223372036854775807l;
+
+        // Current max value is bigger, should fail, but it doesn't
+        Set<ConstraintViolation<MinMaxValidatorsForNumberTest>> res = v.validate(this);
+        assertFalse("Max validation failed", res.isEmpty());
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/NotNullValidatorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/NotNullValidatorTest.java
new file mode 100644
index 0000000..a48150d
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/NotNullValidatorTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.bval.constraints;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+/**
+ * Checks correct behaviour of {@link NotNullValidator}.
+ * <p>
+ * Per the spec:
+ * <ul>
+ * <li>The annotated element must not be null.</li>
+ * </ul>
+ * 
+ * TODO: Mock context and verify that it's not used during validation.
+ * 
+ * @see "bean_validation-1_0_CR1-pfd-spec#Chapter6#Example6.2"
+ * 
+ * @author Carlos Vara
+ */
+public class NotNullValidatorTest extends TestCase {
+
+    public static Test suite() {
+        return new TestSuite(NotNullValidatorTest.class);
+    }
+    
+    public NotNullValidatorTest(String name) {
+    	super(name);
+    }
+    
+    /**
+     * Test {@link NotNullValidator} with null context.
+     */
+    public void testNotNullValidator() {
+    	NotNullValidator nnv = new NotNullValidator();
+    	assertTrue("Non null value validation must succeed", nnv.isValid("hello", null));
+    	assertFalse("Null value validation must fail", nnv.isValid(null, null));    	
+    }
+	
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/NullValidatorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/NullValidatorTest.java
new file mode 100644
index 0000000..7cfe281
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/NullValidatorTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.bval.constraints;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+/**
+ * Checks correct behaviour of {@link NullValidator}.
+ * <p>
+ * Per the spec:
+ * <ul>
+ * <li>The annotated element must be null.</li>
+ * </ul>
+ * 
+ * TODO: Mock context and verify that it's not used during validation.
+ * 
+ * @see "bean_validation-1_0_CR1-pfd-spec#Chapter6#Example6.1"
+ * 
+ * @author Carlos Vara
+ */
+public class NullValidatorTest extends TestCase {
+
+    public static Test suite() {
+        return new TestSuite(NullValidatorTest.class);
+    }
+    
+    public NullValidatorTest(String name) {
+    	super(name);
+    }
+	
+    /**
+     * Test {@link AssertFalseValidator} with null context.
+     */
+    public void testNullValidator() {
+    	NullValidator nv = new NullValidator();
+    	assertTrue("Null value validation must succeed", nv.isValid(null, null));
+    	assertFalse("Non null value validation must fail", nv.isValid("hello", null));    	
+    }
+    
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/Password.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/Password.java
new file mode 100644
index 0000000..0cb2fb9
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/Password.java
@@ -0,0 +1,49 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.lang.annotation.Retention;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * Description: <br/>
+ * User: roman.stumm <br/>
+ * Date: 01.04.2008 <br/>
+ * Time: 12:02:06 <br/>
+ */
+@NotEmpty
+@NotNull
+@Size(min = 4, max = 5)
+@Retention(RUNTIME)
+@Constraint(validatedBy = {})
+// test that Password is validated although only combined constraints exists, no own implementation 
+public @interface Password {
+    Class<?>[] groups() default {};
+
+    String message() default "Wrong password";
+
+    int robustness() default 8;
+
+    Class<? extends Payload>[] payload() default { };
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/ZipCodeCityCoherence.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/ZipCodeCityCoherence.java
new file mode 100644
index 0000000..6c92c7a
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/ZipCodeCityCoherence.java
@@ -0,0 +1,41 @@
+/*
+ * 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.bval.constraints;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+/**
+ * not implemented! simple dummy implemenation for tests only!
+ */
+@Documented
+@Constraint(validatedBy = ZipCodeCityCoherenceValidator.class)
+@Target({TYPE})
+@Retention(RUNTIME)
+public @interface ZipCodeCityCoherence {
+    String message() default "{validator.zipCodeCityCoherence}";    
+    Class<?>[] groups() default {};
+    Class<? extends Payload>[] payload() default { };
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/ZipCodeCityCoherenceValidator.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/ZipCodeCityCoherenceValidator.java
new file mode 100644
index 0000000..92ab6d8
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/constraints/ZipCodeCityCoherenceValidator.java
@@ -0,0 +1,52 @@
+/*
+ * 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.bval.constraints;
+
+
+import org.apache.bval.jsr303.example.ZipCodeCityCarrier;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Description: Class not implemented! simple dummy implemenation for tests only! <br/>
+ * User: roman.stumm <br/>
+ * Date: 01.04.2008 <br/>
+ * Time: 11:45:22 <br/>
+ */
+public class ZipCodeCityCoherenceValidator
+      implements ConstraintValidator<ZipCodeCityCoherence, ZipCodeCityCarrier> {
+    public void initialize(ZipCodeCityCoherence constraintAnnotation) {
+    }
+
+    public boolean isValid(ZipCodeCityCarrier adr, ConstraintValidatorContext context) {
+        boolean r = true;
+        if ("error".equals(adr.getZipCode())) {
+            context.disableDefaultConstraintViolation();
+            context.buildConstraintViolationWithTemplate("zipcode not OK").addConstraintViolation();
+            r = false;
+        }
+        if ("error".equals(adr.getCity())) {
+            context.disableDefaultConstraintViolation();
+            context.buildConstraintViolationWithTemplate("city not OK").addNode("city").addConstraintViolation();
+            r = false;
+        }
+        return r;
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/BeanDescriptorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/BeanDescriptorTest.java
new file mode 100644
index 0000000..c73c6a6
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/BeanDescriptorTest.java
@@ -0,0 +1,235 @@
+/*
+ * 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.bval.jsr303;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import org.apache.bval.jsr303.util.TestUtils;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.NotNull;
+import javax.validation.groups.Default;
+import javax.validation.metadata.*;
+import javax.validation.metadata.ElementDescriptor.ConstraintFinder;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+/**
+ * Tests the implementation of {@link BeanDescriptor} and its dependent
+ * interfaces.
+ * 
+ * @author Carlos Vara
+ */
+public class BeanDescriptorTest extends TestCase {
+    static ValidatorFactory factory;
+
+    static {
+        factory = Validation.buildDefaultValidatorFactory();
+        ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+    }
+
+    /**
+     * Validator instance to test
+     */
+    protected Validator validator;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        validator = createValidator();
+    }
+
+    /**
+     * Create the validator instance.
+     * 
+     * @return Validator
+     */
+    protected Validator createValidator() {
+        return factory.getValidator();
+    }
+
+    /**
+     * Check that groups(), message() and payload() are always in the
+     * attributes.
+     */
+    public void testMandatoryAttributesPresentInConstraintDescriptor() {
+        Set<ConstraintDescriptor<?>> nameDescriptors =
+            validator.getConstraintsForClass(Form.class).getConstraintsForProperty("name").getConstraintDescriptors();
+        Assert.assertEquals("Incorrect number of descriptors", 1, nameDescriptors.size());
+        ConstraintDescriptor<?> nameDescriptor = nameDescriptors.iterator().next();
+        Assert.assertTrue("groups attribute not present", nameDescriptor.getAttributes().containsKey("groups"));
+        Assert.assertTrue("payload attribute not present", nameDescriptor.getAttributes().containsKey("payload"));
+        Assert.assertTrue("message attribute not present", nameDescriptor.getAttributes().containsKey("message"));
+    }
+
+    /**
+     * Check that the groups() attribute value has the correct value when
+     * inheriting groups.
+     */
+    public void testCorrectValueForInheritedGroupsAttribute() {
+        Set<ConstraintDescriptor<?>> passwordDescriptors =
+            validator.getConstraintsForClass(Account.class).getConstraintsForProperty("password")
+                .getConstraintDescriptors();
+        Assert.assertEquals("Incorrect number of descriptors", 1, passwordDescriptors.size());
+        ConstraintDescriptor<?> passwordDescriptor = passwordDescriptors.iterator().next();
+        Assert.assertEquals("Incorrect number of composing constraints", 1, passwordDescriptor
+            .getComposingConstraints().size());
+        ConstraintDescriptor<?> notNullDescriptor = passwordDescriptor.getComposingConstraints().iterator().next();
+
+        // Check that the groups value containts Group1.class
+        Class<?>[] notNullGroups = (Class<?>[]) notNullDescriptor.getAttributes().get("groups");
+        boolean found = false;
+        for (Class<?> group : notNullGroups) {
+            if (group == Group1.class) {
+                found = true;
+                break;
+            }
+        }
+        Assert.assertTrue("Group1 not present in groups attribute", found);
+    }
+
+    /**
+     * Check that the groups() attribute value contains the correct interface as
+     * implicit group when the constraint is defined in that interface instead
+     * of the queried class.
+     */
+    public void testImplicitGroupIsPresent() {
+        Set<ConstraintDescriptor<?>> nameDescriptors =
+            validator.getConstraintsForClass(Woman.class).getConstraintsForProperty("name").getConstraintDescriptors();
+        Assert.assertEquals("Incorrect number of descriptors", 1, nameDescriptors.size());
+        ConstraintDescriptor<?> notNullDescriptor = nameDescriptors.iterator().next();
+
+        // Check that the groups attribute value contains the implicit group
+        // Person and the Default group
+        Class<?>[] notNullGroups = (Class<?>[]) notNullDescriptor.getAttributes().get("groups");
+        Assert.assertEquals("Incorrect number of groups", 2, notNullGroups.length);
+        Assert.assertTrue("Default group not present", notNullGroups[0].equals(Default.class)
+            || notNullGroups[1].equals(Default.class));
+        Assert.assertTrue("Implicit group not present", notNullGroups[0].equals(Person.class)
+            || notNullGroups[1].equals(Person.class));
+    }
+
+    /**
+     * Check that the groups() attribute value does not contain the implicit
+     * interface group when querying the interface directly.
+     */
+    public void testNoImplicitGroupWhenQueryingInterfaceDirectly() {
+        Set<ConstraintDescriptor<?>> nameDescriptors =
+            validator.getConstraintsForClass(Person.class).getConstraintsForProperty("name").getConstraintDescriptors();
+        Assert.assertEquals("Incorrect number of descriptors", 1, nameDescriptors.size());
+        ConstraintDescriptor<?> notNullDescriptor = nameDescriptors.iterator().next();
+
+        // Check that only the default group is present
+        Class<?>[] notNullGroups = (Class<?>[]) notNullDescriptor.getAttributes().get("groups");
+        Assert.assertEquals("Incorrect number of groups", 1, notNullGroups.length);
+        Assert.assertTrue("Default group not present", notNullGroups[0].equals(Default.class));
+    }
+
+    /**
+     * Check that the implementations of
+     * {@link ElementDescriptor#getElementClass()} work as defined in the spec.
+     */
+    public void testElementDescriptorGetElementClass() {
+        BeanDescriptor beanDescriptor = validator.getConstraintsForClass(Person.class);
+        Assert.assertEquals("Incorrect class returned", Person.class, beanDescriptor.getElementClass());
+
+        PropertyDescriptor nameDescriptor = beanDescriptor.getConstraintsForProperty("name");
+        Assert.assertEquals("Incorrect class returned", String.class, nameDescriptor.getElementClass());
+    }
+
+    /**
+     * Check the correct behavior of
+     * {@link ConstraintFinder#lookingAt(javax.validation.metadata.Scope)}.
+     */
+    public void testConstraintFinderLookingAt() {
+        PropertyDescriptor nameDescriptor =
+            validator.getConstraintsForClass(Woman.class).getConstraintsForProperty("name");
+        Set<ConstraintDescriptor<?>> constraints =
+            nameDescriptor.findConstraints().lookingAt(Scope.HIERARCHY).getConstraintDescriptors();
+        Assert.assertEquals("Incorrect number of descriptors", 1, constraints.size());
+
+        constraints = nameDescriptor.findConstraints().lookingAt(Scope.LOCAL_ELEMENT).getConstraintDescriptors();
+        Assert.assertEquals("Incorrect number of descriptors", 0, constraints.size());
+        TestUtils.failOnModifiable(constraints, "constraintFinder constraintDescriptors");
+        //verify that changes to one ConstraintFinder don't affect the base:
+        constraints = nameDescriptor.getConstraintDescriptors();
+        Assert.assertEquals("Incorrect number of descriptors", 1, constraints.size());
+    }
+
+    public static class Form {
+        @NotNull
+        public String name;
+    }
+
+    public static class Account {
+        @Password(groups = { Group1.class })
+        public String password;
+    }
+
+    @NotNull(groups = {})
+    @Constraint(validatedBy = {})
+    @Documented
+    @Target( { METHOD, FIELD, TYPE })
+    @Retention(RUNTIME)
+    public static @interface Password {
+        String message() default "Invalid password";
+
+        Class<?>[] groups() default {};
+
+        Class<? extends Payload>[] payload() default {};
+    }
+
+    public static interface Group1 {
+    }
+
+    public static class Woman implements Person {
+
+        private String name;
+
+        public String getName() {
+            return this.name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+
+    public static interface Person {
+        @NotNull
+        String getName();
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/BootstrapTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/BootstrapTest.java
new file mode 100644
index 0000000..4745489
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/BootstrapTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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.bval.jsr303;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.Configuration;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.ValidationException;
+import javax.validation.ValidationProviderResolver;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.bootstrap.ProviderSpecificBootstrap;
+import javax.validation.spi.ValidationProvider;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.bval.constraints.NotNullValidator;
+import org.apache.bval.jsr303.example.Customer;
+
+/**
+ * Description: <br/>
+ */
+public class BootstrapTest extends TestCase {
+    public void testDirectBootstrap() {
+        Validator validator = ApacheValidatorFactory.getDefault().getValidator();
+        Assert.assertNotNull(validator);
+        Assert.assertTrue(ApacheValidatorFactory.getDefault() == ApacheValidatorFactory.getDefault());
+    }
+
+    public void testEverydayBootstrap() {
+        ApacheValidatorFactory factory = (ApacheValidatorFactory) Validation.buildDefaultValidatorFactory();
+        Validator validator = factory.getValidator();
+        Assert.assertNotNull(validator);
+
+        // each call to Validation.getValidationBuilder() returns a new builder
+        // with new state
+        ApacheValidatorFactory factory2 = (ApacheValidatorFactory) Validation.buildDefaultValidatorFactory();
+        Assert.assertTrue(factory2 != factory);
+        Assert.assertTrue(factory2.getMessageInterpolator() != factory.getMessageInterpolator());
+
+    }
+
+    public void testLocalizedMessageInterpolatorFactory() {
+        Configuration<?> builder = Validation.byDefaultProvider().configure();
+        // changing the builder allows to create different factories
+        DefaultMessageInterpolator interpolator = new DefaultMessageInterpolator();
+        builder.messageInterpolator(interpolator);
+        ApacheValidatorFactory factory = (ApacheValidatorFactory) builder.buildValidatorFactory();
+
+        // ALTERNATIVE:
+        // you could do it without modifying the builder or reusing it,
+        // but then you need to use bval-core proprietary APIs:
+        ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+        // now factory's message resolver is using the english locale
+    }
+
+    /**
+     * some tests based on RI tested behaviors to ensure our implementation
+     * works as the reference implementation
+     */
+
+    public void testCustomConstraintFactory() {
+
+        Configuration<?> builder = Validation.byDefaultProvider().configure();
+        assertDefaultBuilderAndFactory(builder);
+
+        ValidatorFactory factory = builder.buildValidatorFactory();
+        Validator validator = factory.getValidator();
+
+        Customer customer = new Customer();
+        customer.setFirstName("John");
+
+        Set<ConstraintViolation<Customer>> ConstraintViolations = validator.validate(customer);
+        Assert.assertFalse(ConstraintViolations.isEmpty());
+
+        builder = Validation.byDefaultProvider().configure();
+        builder.constraintValidatorFactory(new ConstraintValidatorFactory() {
+            public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
+                if (key == NotNullValidator.class) {
+                    @SuppressWarnings("unchecked")
+                    final T result = (T) new BadlyBehavedNotNullValidator();
+                    return result;
+                }
+                return new DefaultConstraintValidatorFactory().getInstance(key);
+            }
+        });
+        factory = builder.buildValidatorFactory();
+        validator = factory.getValidator();
+        Set<ConstraintViolation<Customer>> ConstraintViolations2 = validator.validate(customer);
+        Assert.assertTrue("Wrong number of constraints", ConstraintViolations.size() > ConstraintViolations2.size());
+    }
+
+    public void testCustomResolverAndType() {
+        ValidationProviderResolver resolver = new ValidationProviderResolver() {
+
+            public List<ValidationProvider<?>> getValidationProviders() {
+                List<ValidationProvider<?>> list = new ArrayList<ValidationProvider<?>>(1);
+                list.add(new ApacheValidationProvider());
+                return list;
+            }
+        };
+
+        ApacheValidatorConfiguration builder =
+            Validation.byProvider(ApacheValidationProvider.class).providerResolver(resolver).configure();
+        assertDefaultBuilderAndFactory(builder);
+    }
+
+    public void testCustomResolver() {
+        ValidationProviderResolver resolver = new ValidationProviderResolver() {
+
+            public List<ValidationProvider<?>> getValidationProviders() {
+                return Collections.<ValidationProvider<?>> singletonList(new ApacheValidationProvider());
+            }
+        };
+
+        Configuration<?> builder = Validation.byDefaultProvider().providerResolver(resolver).configure();
+        assertDefaultBuilderAndFactory(builder);
+    }
+
+    private void assertDefaultBuilderAndFactory(Configuration<?> builder) {
+        Assert.assertNotNull(builder);
+        Assert.assertTrue(builder instanceof ConfigurationImpl);
+
+        ValidatorFactory factory = builder.buildValidatorFactory();
+        Assert.assertNotNull(factory);
+        Assert.assertTrue(factory instanceof ApacheValidatorFactory);
+    }
+
+    public void testFailingCustomResolver() {
+        ValidationProviderResolver resolver = new ValidationProviderResolver() {
+
+            public List<ValidationProvider<?>> getValidationProviders() {
+                return Collections.emptyList();
+            }
+        };
+
+        ProviderSpecificBootstrap<ApacheValidatorConfiguration> type =
+            Validation.byProvider(ApacheValidationProvider.class);
+
+        final ProviderSpecificBootstrap<ApacheValidatorConfiguration> specializedBuilderFactory =
+            type.providerResolver(resolver);
+
+        try {
+            specializedBuilderFactory.configure();
+            Assert.fail();
+        } catch (ValidationException e) {
+            Assert.assertTrue("Wrong error message", e.getMessage().contains("provider")
+                && e.getMessage().contains("org.apache.bval.jsr303.ApacheValidationProvider"));
+        }
+    }
+
+    class BadlyBehavedNotNullValidator extends NotNullValidator {
+        @Override
+        public boolean isValid(Object object, ConstraintValidatorContext context) {
+            return true;
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CircularReferencesTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CircularReferencesTest.java
new file mode 100644
index 0000000..5b8dd88
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CircularReferencesTest.java
@@ -0,0 +1,116 @@
+/*
+ *  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.bval.jsr303;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import javax.validation.*;
+import javax.validation.constraints.Size;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Checks that circular references in the bean graph are correctly detected when
+ * validating.
+ * 
+ * @author Carlos Vara
+ */
+public class CircularReferencesTest extends TestCase {
+    static ValidatorFactory factory;
+
+    static {
+        factory = Validation.buildDefaultValidatorFactory();
+        ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+    }
+
+    /**
+     * Validator instance to test
+     */
+    protected Validator validator;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        validator = createValidator();
+    }
+
+    /**
+     * Create the validator instance.
+     * 
+     * @return Validator
+     */
+    protected Validator createValidator() {
+        return factory.getValidator();
+    }
+
+    /**
+     * Checks that validation correctly stops when finding a circular
+     * dependency.
+     */
+    public void testAutoreferringBean() {
+        Person p1 = new Person();
+        p1.name = "too-long-name";
+        p1.sibling = p1;
+
+        Set<ConstraintViolation<Person>> violations = validator.validate(p1);
+
+        Assert.assertEquals("Only 1 violation should be reported", 1, violations.size());
+        ConstraintViolation<Person> violation = violations.iterator().next();
+        Assert.assertEquals("Incorrect violation path", "name", violation.getPropertyPath().toString());
+    }
+
+    /**
+     * Checks that a bean is always validated when appearing in non-circular
+     * paths inside the bean graph.
+     */
+    public void testNonCircularArrayOfSameBean() {
+        Boss boss = new Boss();
+        Person p1 = new Person();
+        p1.name = "too-long-name";
+
+        boss.employees = new Person[] { p1, p1, p1, p1 };
+
+        Set<ConstraintViolation<Boss>> violations = validator.validate(boss);
+
+        Assert.assertEquals("A total of 4 violations should be reported", 4, violations.size());
+    }
+
+    public static class Person {
+
+        @Valid
+        public Person sibling;
+
+        @Size(max = 10)
+        public String name;
+
+    }
+
+    public static class Boss {
+
+        @Valid
+        public Person[] employees;
+
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ComposedConstraintsTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ComposedConstraintsTest.java
new file mode 100644
index 0000000..538b9a1
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ComposedConstraintsTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.bval.jsr303;
+
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.Size;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.bval.jsr303.example.CompanyAddress;
+import org.apache.bval.jsr303.example.FrenchAddress;
+import org.apache.bval.jsr303.util.TestUtils;
+
+/**
+ * Description: <br/>
+ */
+public class ComposedConstraintsTest extends TestCase {
+    static ValidatorFactory factory;
+
+    static {
+        factory = Validation.buildDefaultValidatorFactory();
+        ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+    }
+
+    /**
+     * Validator instance to test
+     */
+    protected Validator validator;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        validator = createValidator();
+    }
+
+    /**
+     * Create the validator instance.
+     * 
+     * @return Validator
+     */
+    protected Validator createValidator() {
+        return factory.getValidator();
+    }
+
+    public void testMetaDataAPI_ComposedConstraints() {
+        ElementDescriptor ed =
+              validator.getConstraintsForClass(FrenchAddress.class)
+                    .getConstraintsForProperty("zipCode");
+        Assert.assertEquals(1, ed.getConstraintDescriptors().size());
+        for (ConstraintDescriptor<?> cd : ed.getConstraintDescriptors()) {
+            Assert.assertTrue(cd.isReportAsSingleViolation());
+            Assert.assertEquals(3, cd.getComposingConstraints().size());
+            Assert.assertTrue("no composing constraints found!!",
+                  !cd.getComposingConstraints().isEmpty());
+            processConstraintDescriptor(cd); //check all constraints on zip code
+        }
+    }
+
+    public void processConstraintDescriptor(ConstraintDescriptor<?> cd) {
+        //Size.class is understood by the tool
+        if (cd.getAnnotation().annotationType().equals(Size.class)) {
+            @SuppressWarnings("unused")
+            Size m = (Size) cd.getAnnotation();//what for?
+        }
+        for (ConstraintDescriptor<?> composingCd : cd.getComposingConstraints()) {
+            //check composing constraints recursively
+            processConstraintDescriptor(composingCd);
+        }
+    }
+
+    public void testValidateComposed() {
+        FrenchAddress adr = new FrenchAddress();
+        Set<ConstraintViolation<FrenchAddress>> findings = validator.validate(adr);
+        Assert.assertEquals(1, findings.size()); // with @ReportAsSingleConstraintViolation
+
+        ConstraintViolation<FrenchAddress> finding = findings.iterator().next();
+        Assert.assertEquals("Wrong zipcode", finding.getMessage());
+
+        adr.setZipCode("1234567");
+        findings = validator.validate(adr);
+        Assert.assertEquals(0, findings.size());
+
+        adr.setZipCode("1234567234567");
+        findings = validator.validate(adr);
+        Assert.assertTrue(findings.size() > 0); // too long
+    }
+
+    public void testOverridesAttributeConstraintIndex() {
+        CompanyAddress adr = new CompanyAddress("invalid-string");
+        Set<ConstraintViolation<CompanyAddress>> findings = validator.validate(adr);
+        assertEquals(2, findings.size()); // without @ReportAsSingleConstraintViolation
+        assertNotNull(TestUtils.getViolationWithMessage(findings, "Not COMPANY"));
+        assertNotNull(TestUtils.getViolationWithMessage(findings, "Not an email"));
+
+        adr =  new CompanyAddress("JOHN_DO@WEB.DE");
+        findings = validator.validate(adr);
+        assertEquals(1, findings.size());
+        assertNotNull(TestUtils.getViolationWithMessage(findings, "Not COMPANY"));
+
+        adr =  new CompanyAddress("JOHN_DO@COMPANY.DE");
+        findings = validator.validate(adr);
+        Assert.assertTrue(findings.isEmpty());
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintCompositionTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintCompositionTest.java
new file mode 100644
index 0000000..fc24889
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintCompositionTest.java
@@ -0,0 +1,306 @@
+/*
+ *  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.bval.jsr303;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintViolation;
+import javax.validation.OverridesAttribute;
+import javax.validation.Payload;
+import javax.validation.ReportAsSingleViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+import javax.validation.metadata.ConstraintDescriptor;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+/**
+ * Checks that groups are correctly inherited from the root constraint to its
+ * compositing constraints.
+ * 
+ * @author Carlos Vara
+ */
+public class ConstraintCompositionTest extends TestCase {
+    static ValidatorFactory factory;
+
+    static {
+        factory = Validation.buildDefaultValidatorFactory();
+        ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+    }
+
+    /**
+     * Validator instance to test
+     */
+    protected Validator validator;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        validator = createValidator();
+    }
+
+    /**
+     * Create the validator instance.
+     * 
+     * @return Validator
+     */
+    protected Validator createValidator() {
+        return factory.getValidator();
+    }
+
+    /**
+     * Check correct group inheritance on constraint composition on a 1 level
+     * hierarchy.
+     */
+    public void test1LevelInheritance() {
+        Set<ConstraintViolation<Person>> violations = validator.validate(new Person());
+
+        Assert.assertEquals("Wrong number of violations detected", 1, violations.size());
+        String msg = violations.iterator().next().getMessage();
+        Assert.assertEquals("Incorrect violation message", "A person needs a non null name", msg);
+
+        violations = validator.validate(new Person(), Group1.class);
+        Assert.assertEquals("Wrong number of violations detected", 0, violations.size());
+    }
+
+    /**
+     * Check correct group inheritance on constraint composition on a 2 level
+     * hierarchy.
+     */
+    public void test2LevelInheritance() {
+        Set<ConstraintViolation<Man>> violations = validator.validate(new Man());
+
+        Assert.assertEquals("Wrong number of violations detected", 0, violations.size());
+
+        violations = validator.validate(new Man(), Group1.class);
+        Assert.assertEquals("Wrong number of violations detected", 1, violations.size());
+        String msg = violations.iterator().next().getMessage();
+        Assert.assertEquals("Incorrect violation message", "A person needs a non null name", msg);
+    }
+
+    /**
+     * Checks that the groups() value of the constraint annotations are
+     * correctly set to the inherited ones.
+     */
+    public void testAnnotationGroupsAreInherited() {
+        // Check that the groups() value is right when querying the metadata
+        ConstraintDescriptor<?> manNameDesc =
+            validator.getConstraintsForClass(Man.class).getConstraintsForProperty("name").getConstraintDescriptors()
+                .iterator().next();
+        ConstraintDescriptor<?> personNameDesc = manNameDesc.getComposingConstraints().iterator().next();
+        ConstraintDescriptor<?> notNullDesc = personNameDesc.getComposingConstraints().iterator().next();
+        Assert.assertEquals("There should only be 1 group", 1, manNameDesc.getGroups().size());
+        Assert.assertTrue("Group1 should be present", manNameDesc.getGroups().contains(Group1.class));
+        Assert.assertEquals("There should only be 1 group", 1, personNameDesc.getGroups().size());
+        Assert.assertTrue("Group1 should be present", personNameDesc.getGroups().contains(Group1.class));
+        Assert.assertEquals("There should only be 1 group", 1, personNameDesc.getGroups().size());
+        Assert.assertTrue("Group1 should be present", notNullDesc.getGroups().contains(Group1.class));
+
+        // Check that the groups() value is right when accessing it from an
+        // error
+        Set<ConstraintViolation<Man>> violations = validator.validate(new Man(), Group1.class);
+        Set<Class<?>> notNullGroups = violations.iterator().next().getConstraintDescriptor().getGroups();
+        Assert.assertEquals("There should only be 1 group", 1, notNullGroups.size());
+        Assert.assertTrue("Group1 should be the only group", notNullGroups.contains(Group1.class));
+    }
+
+    /**
+     * Checks that the payload() value of the constraint annotations are
+     * correctly set to the inherited ones.
+     */
+    public void testAnnotationPayloadsAreInherited() {
+        // Check that the payload() value is right when querying the metadata
+        ConstraintDescriptor<?> manNameDesc =
+            validator.getConstraintsForClass(Man.class).getConstraintsForProperty("name").getConstraintDescriptors()
+                .iterator().next();
+        ConstraintDescriptor<?> personNameDesc = manNameDesc.getComposingConstraints().iterator().next();
+        ConstraintDescriptor<?> notNullDesc = personNameDesc.getComposingConstraints().iterator().next();
+        Assert.assertEquals("There should only be 1 payload class", 1, manNameDesc.getPayload().size());
+        Assert.assertTrue("Payload1 should be present", manNameDesc.getPayload().contains(Payload1.class));
+        Assert.assertEquals("There should only be 1 payload class", 1, personNameDesc.getPayload().size());
+        Assert.assertTrue("Payload1 should be present", personNameDesc.getPayload().contains(Payload1.class));
+        Assert.assertEquals("There should only be 1 payload class", 1, personNameDesc.getPayload().size());
+        Assert.assertTrue("Payload1 should be present", notNullDesc.getPayload().contains(Payload1.class));
+
+        // Check that the payload() value is right when accessing it from an
+        // error
+        Set<ConstraintViolation<Man>> violations = validator.validate(new Man(), Group1.class);
+        Set<Class<? extends Payload>> notNullPayload =
+            violations.iterator().next().getConstraintDescriptor().getPayload();
+        Assert.assertEquals("There should only be 1 payload class", 1, notNullPayload.size());
+        Assert.assertTrue("Payload1 should be the only payload", notNullPayload.contains(Payload1.class));
+    }
+
+    /**
+     * Checks that {@link OverridesAttribute#constraintIndex()} parsing and
+     * applying works.
+     */
+    public void testIndexedOverridesAttributes() {
+        Person p = new Person();
+        p.name = "valid";
+
+        // With a valid id, no errors expected
+        p.id = "1234";
+        Set<ConstraintViolation<Person>> constraintViolations = validator.validate(p);
+        Assert.assertTrue("No violations should be reported on valid id", constraintViolations.isEmpty());
+
+        // With a short id, only 1 error expected
+        p.id = "1";
+        constraintViolations = validator.validate(p);
+        Assert.assertEquals("Only 1 violation expected", 1, constraintViolations.size());
+        ConstraintViolation<Person> violation = constraintViolations.iterator().next();
+        Assert.assertEquals("Wrong violation", "Id is too short", violation.getMessage());
+
+        // With a long id, only 1 error expected
+        p.id = "loooooong id";
+        constraintViolations = validator.validate(p);
+        Assert.assertEquals("Only 1 violation expected", 1, constraintViolations.size());
+        violation = constraintViolations.iterator().next();
+        Assert.assertEquals("Wrong violation", "Id is too long", violation.getMessage());
+    }
+
+    /**
+     * Checks that errors are reported correctly when using
+     * {@link ReportAsSingleViolation}.
+     */
+    public void testReportAsAsingleViolation() {
+        Code c = new Code();
+        c.code = "very invalid code";
+        Set<ConstraintViolation<Code>> constraintViolations = validator.validate(c);
+
+        // Only 1 error expected
+        Assert.assertEquals("Only 1 violation expected", 1, constraintViolations.size());
+        ConstraintViolation<Code> violation = constraintViolations.iterator().next();
+        Assert.assertEquals("Wrong violation message", "Invalid code", violation.getMessage());
+        Assert.assertEquals("Wrong violation type", ElevenDigitsCode.class, ((Annotation) violation
+            .getConstraintDescriptor().getAnnotation()).annotationType());
+    }
+
+    public static class Person {
+        @PersonName
+        String name;
+
+        @PersonId
+        String id;
+    }
+
+    public static class Man {
+        @ManName(groups = { Group1.class }, payload = { Payload1.class })
+        String name;
+    }
+
+    public static class Code {
+        @ElevenDigitsCode
+        String code;
+    }
+
+    @NotNull(message = "A person needs a non null name", groups = { Group1.class }, payload = {})
+    @Constraint(validatedBy = {})
+    @Documented
+    @Target( { METHOD, FIELD, TYPE })
+    @Retention(RUNTIME)
+    public static @interface PersonName {
+        String message() default "Wrong person name";
+
+        Class<?>[] groups() default {};
+
+        Class<? extends Payload>[] payload() default {};
+    }
+
+    @PersonName(groups = { Group2.class }, payload = { Payload1.class, Payload2.class })
+    @Constraint(validatedBy = {})
+    @Documented
+    @Target( { METHOD, FIELD, TYPE })
+    @Retention(RUNTIME)
+    public static @interface ManName {
+        String message() default "Wrong man name";
+
+        Class<?>[] groups() default {};
+
+        Class<? extends Payload>[] payload() default {};
+    }
+
+    @Size.List( { @Size(min = 3, max = 3, message = "Id is too short"),
+        @Size(min = 5, max = 5, message = "Id is too long") })
+    @Constraint(validatedBy = {})
+    @Documented
+    @Target( { METHOD, FIELD, TYPE })
+    @Retention(RUNTIME)
+    public static @interface PersonId {
+        String message() default "Wrong person id";
+
+        Class<?>[] groups() default {};
+
+        Class<? extends Payload>[] payload() default {};
+
+        @OverridesAttribute(constraint = Size.class, constraintIndex = 0, name = "max")
+        int maxSize() default 1000;
+
+        @OverridesAttribute(constraint = Size.class, constraintIndex = 1, name = "min")
+        int minSize() default 0;
+    }
+
+    @Size(min = 11, max = 11)
+    @Pattern(regexp = "\\d*")
+    @Constraint(validatedBy = {})
+    @ReportAsSingleViolation
+    @Documented
+    @Target( { METHOD, FIELD, TYPE })
+    @Retention(RUNTIME)
+    public static @interface ElevenDigitsCode {
+        String message() default "Invalid code";
+
+        Class<?>[] groups() default {};
+
+        Class<? extends Payload>[] payload() default {};
+    }
+
+    public static interface Group1 {
+    }
+
+    public static interface Group2 {
+    }
+
+    public static class Payload1 implements Payload {
+    }
+
+    public static class Payload2 implements Payload {
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintDefinitionsTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintDefinitionsTest.java
new file mode 100644
index 0000000..3729370
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintDefinitionsTest.java
@@ -0,0 +1,322 @@
+/*
+ * 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.bval.jsr303;
+
+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 java.util.Locale;
+import java.util.Set;
+
+import javax.validation.Constraint;
+import javax.validation.ConstraintDefinitionException;
+import javax.validation.Payload;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.Min;
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.ConstraintDescriptor;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.bval.constraints.NotNullValidator;
+
+/**
+ * Checks the correct parsing of constraint definitions.
+ * 
+ * @author Carlos Vara
+ */
+public class ConstraintDefinitionsTest extends TestCase {
+    static ValidatorFactory factory;
+
+    static {
+        factory = Validation.buildDefaultValidatorFactory();
+        ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+    }
+
+    /**
+     * Validator instance to test
+     */
+    protected Validator validator;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        validator = createValidator();
+    }
+
+    /**
+     * Create the validator instance.
+     * 
+     * @return Validator
+     */
+    protected Validator createValidator() {
+        return factory.getValidator();
+    }
+
+    /**
+     * Checks the correct parsing of a constraint with an array of constraints
+     * as attributes.
+     */
+    public void testCustomAttributes() {
+        BeanDescriptor constraints = validator.getConstraintsForClass(Person.class);
+        Set<ConstraintDescriptor<?>> ageConstraints =
+            constraints.getConstraintsForProperty("age").getConstraintDescriptors();
+
+        Assert.assertEquals("There should be 2 constraints in 'age'", ageConstraints.size(), 2);
+        for (ConstraintDescriptor<?> cd : ageConstraints) {
+            Assert.assertEquals("Annotation should be @Min", cd.getAnnotation().annotationType().getName(), Min.class
+                .getName());
+        }
+    }
+
+    /**
+     * Checks that a {@link ConstraintDefinitionException} is thrown when
+     * parsing a constraint definition with no <code>groups()</code> method.
+     */
+    public void testNoGroupsConstraint() {
+        try {
+            validator.validate(new NoGroups());
+            fail("No exception thrown when parsing a constraint definition with no groups() method");
+        } catch (ConstraintDefinitionException e) {
+            // correct
+        }
+    }
+
+    /**
+     * Checks that a {@link ConstraintDefinitionException} is thrown when
+     * parsing a constraint definition with an invalid <code>groups()</code>
+     * method.
+     */
+    public void testInvalidDefaultGroupsConstraint() {
+        try {
+            validator.validate(new InvalidGroups());
+            fail("No exception thrown when parsing a constraint definition with a groups() method does not return Class[]");
+        } catch (ConstraintDefinitionException e) {
+            // correct
+        }
+    }
+
+    /**
+     * Checks that a {@link ConstraintDefinitionException} is thrown when
+     * parsing a constraint definition with no <code>payload()</code> method.
+     */
+    public void testNoPayloadConstraint() {
+        try {
+            validator.validate(new NoPayload());
+            fail("No exception thrown when parsing a constraint definition with no payload() method");
+        } catch (ConstraintDefinitionException e) {
+            // correct
+        }
+    }
+
+    /**
+     * Checks that a {@link ConstraintDefinitionException} is thrown when
+     * parsing a constraint definition with an invalid <code>payload()</code>
+     * method.
+     */
+    public void testInvalidDefaultPayloadConstraint() {
+        try {
+            validator.validate(new InvalidPayload());
+            fail("No exception thrown when parsing a constraint definition with a payload() method does not return an empty array");
+        } catch (ConstraintDefinitionException e) {
+            // correct
+        }
+    }
+
+    /**
+     * Checks that a {@link ConstraintDefinitionException} is thrown when
+     * parsing a constraint definition with no <code>message()</code> method.
+     */
+    public void testNoMessageConstraint() {
+        try {
+            validator.validate(new NoMessage());
+            fail("No exception thrown when parsing a constraint definition with no payload() method");
+        } catch (ConstraintDefinitionException e) {
+            // correct
+        }
+    }
+
+    /**
+     * Checks that a {@link ConstraintDefinitionException} is thrown when
+     * parsing a constraint definition with an invalid <code>message()</code>
+     * method.
+     */
+    public void testInvalidDefaultMessageConstraint() {
+        try {
+            validator.validate(new InvalidMessage());
+            fail("No exception thrown when parsing a constraint definition with a message() method does not return a String");
+        } catch (ConstraintDefinitionException e) {
+            // correct
+        }
+    }
+
+    /**
+     * Checks that a {@link ConstraintDefinitionException} is thrown when
+     * parsing a constraint definition with a method starting with 'valid'.
+     */
+    public void testInvalidAttributeConstraint() {
+        try {
+            validator.validate(new InvalidAttribute());
+            fail("No exception thrown when parsing a constraint definition with a method starting with 'valid'");
+        } catch (ConstraintDefinitionException e) {
+            // correct
+        }
+    }
+
+    public static class Person {
+        @MinList( { @Min(value = 20), @Min(value = 30) })
+        public Integer age;
+    }
+
+    @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+    @Retention(RUNTIME)
+    @Documented
+    public static @interface MinList {
+        Min[] value();
+    }
+
+    public static class NoGroups {
+        @NoGroupsConstraint
+        public String prop;
+    }
+
+    @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+    @Retention(RUNTIME)
+    @Documented
+    @Constraint(validatedBy = { NotNullValidator.class })
+    public static @interface NoGroupsConstraint {
+        String message() default "def msg";
+
+        Class<? extends Payload>[] payload() default {};
+    }
+
+    public static class InvalidGroups {
+        @InvalidGroupsConstraint
+        public String prop;
+    }
+
+    @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+    @Retention(RUNTIME)
+    @Documented
+    @Constraint(validatedBy = { NotNullValidator.class })
+    public static @interface InvalidGroupsConstraint {
+        String message() default "def msg";
+
+        String[] groups() default { "Group1" };
+
+        Class<? extends Payload>[] payload() default {};
+    }
+
+    public static class NoPayload {
+        @NoPayloadConstraint
+        public String prop;
+    }
+
+    @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+    @Retention(RUNTIME)
+    @Documented
+    @Constraint(validatedBy = { NotNullValidator.class })
+    public static @interface NoPayloadConstraint {
+        String message() default "def msg";
+
+        String[] groups() default {};
+    }
+
+    public static class InvalidPayload {
+        @InvalidPayloadConstraint
+        public String prop;
+    }
+
+    @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+    @Retention(RUNTIME)
+    @Documented
+    @Constraint(validatedBy = { NotNullValidator.class })
+    public static @interface InvalidPayloadConstraint {
+        String message() default "def msg";
+
+        String[] groups() default {};
+
+        Class<? extends Payload>[] payload() default { Payload1.class };
+
+        public static class Payload1 implements Payload {
+        }
+    }
+
+    public static class NoMessage {
+        @NoMessageConstraint
+        public String prop;
+    }
+
+    @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+    @Retention(RUNTIME)
+    @Documented
+    @Constraint(validatedBy = { NotNullValidator.class })
+    public static @interface NoMessageConstraint {
+        String[] groups() default {};
+
+        Class<? extends Payload>[] payload() default {};
+    }
+
+    public static class InvalidMessage {
+        @InvalidMessageConstraint(message = 2)
+        public String prop;
+    }
+
+    @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+    @Retention(RUNTIME)
+    @Documented
+    @Constraint(validatedBy = { NotNullValidator.class })
+    public static @interface InvalidMessageConstraint {
+        int message();
+
+        String[] groups() default {};
+
+        Class<? extends Payload>[] payload() default {};
+    }
+
+    public static class InvalidAttribute {
+        @InvalidAttributeConstraint
+        public String prop;
+    }
+
+    @Target( { METHOD, FIELD, ANNOTATION_TYPE })
+    @Retention(RUNTIME)
+    @Documented
+    @Constraint(validatedBy = { NotNullValidator.class })
+    public static @interface InvalidAttributeConstraint {
+        String message() default "def msg";
+
+        String[] groups() default {};
+
+        Class<? extends Payload>[] payload() default {};
+
+        String validValue() default "1";
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintValidatorContextTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintValidatorContextTest.java
new file mode 100644
index 0000000..771528c
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ConstraintValidatorContextTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.bval.jsr303;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.util.PathImpl;
+import org.apache.bval.model.ValidationListener;
+import org.apache.bval.model.ValidationListener.Error;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorContext.ConstraintViolationBuilder;
+
+/**
+ * Checks to validate the correct implementation of
+ * {@link ConstraintValidatorContext} and its sub-interfaces.
+ * 
+ * @author Carlos Vara
+ */
+public class ConstraintValidatorContextTest extends TestCase {
+
+    private ConstraintValidatorContextImpl cvc;
+    private ConstraintViolationBuilder cvb;
+
+    @Mock
+    private GroupValidationContext<ValidationListener> groupValidationContext;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        Mockito.when(groupValidationContext.getPropertyPath()).thenAnswer(new Answer<PathImpl>() {
+
+            public PathImpl answer(InvocationOnMock invocation) throws Throwable {
+                return PathImpl.createPathFromString("");
+            }
+        });
+        this.cvc = new ConstraintValidatorContextImpl(groupValidationContext,
+                null);
+        this.cvc.disableDefaultConstraintViolation();
+        this.cvb = cvc.buildConstraintViolationWithTemplate("dummy.msg.tpl");
+    }
+
+    public void testPerson1() {
+        cvb.addNode("person").addNode(null).inIterable().atIndex(1)
+                .addConstraintViolation();
+        Error error = cvc.getErrorMessages().iterator().next();
+        PathImpl errorPath = (PathImpl) error.getOwner();
+        Assert.assertEquals("Incorrect path created", "person[1]", errorPath
+                .toString());
+    }
+
+    public void testPersonLawyerName() {
+        cvb.addNode("person").addNode("name").inIterable().atKey("john")
+                .addConstraintViolation();
+        Error error = cvc.getErrorMessages().iterator().next();
+        PathImpl errorPath = (PathImpl) error.getOwner();
+        Assert.assertEquals("Incorrect path created", "person[john].name",
+                errorPath.toString());
+    }
+
+    public void test0Name() {
+        cvb.addNode(null).addNode("name").inIterable().atIndex(0).addNode(null)
+                .inIterable().addConstraintViolation();
+        Error error = cvc.getErrorMessages().iterator().next();
+        PathImpl errorPath = (PathImpl) error.getOwner();
+        Assert.assertEquals("Incorrect path created", "[0].name[]", errorPath
+                .toString());
+    }
+
+    public void testEmptyIndex() {
+        cvb.addNode(null).addNode(null).inIterable().addConstraintViolation();
+        Error error = cvc.getErrorMessages().iterator().next();
+        PathImpl errorPath = (PathImpl) error.getOwner();
+        Assert.assertEquals("Incorrect path created", "[]", errorPath
+                .toString());
+    }
+
+    public void testRootPath() {
+        // Adding only nulls should still give a root path
+        cvb.addNode(null).addNode(null).addNode(null).addNode(null)
+                .addConstraintViolation();
+        Error error = cvc.getErrorMessages().iterator().next();
+        PathImpl errorPath = (PathImpl) error.getOwner();
+        Assert.assertTrue("Created path must be a root path", errorPath
+                .isRootPath());
+
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CustomConstraintValidatorFactoryTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CustomConstraintValidatorFactoryTest.java
new file mode 100644
index 0000000..a9babfb
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CustomConstraintValidatorFactoryTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.bval.jsr303;
+
+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.ElementType.TYPE;
+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.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import javax.validation.ConstraintValidatorFactory;
+import javax.validation.Payload;
+import javax.validation.Validation;
+import javax.validation.ValidationException;
+import javax.validation.Validator;
+
+import org.apache.bval.jsr303.CustomConstraintValidatorFactoryTest.GoodPerson.GoodPersonValidator;
+
+import junit.framework.TestCase;
+
+/**
+ * Checks that overriding the default {@link ConstraintValidatorFactory} works
+ * as expected.
+ * 
+ * @author Carlos Vara
+ */
+public class CustomConstraintValidatorFactoryTest extends TestCase {
+
+    /**
+     * If the custom ConstraintValidatorFactory returns <code>null</code> for a
+     * valid {@link ConstraintValidatorFactory#getInstance(Class)} call, a
+     * validation exception should be thrown.
+     */
+    public void testValidationExceptionWhenFactoryReturnsNullValidator() {
+        
+        ConstraintValidatorFactory customFactory = new ConstraintValidatorFactory() {
+            // @Override - not allowed in 1.5 for Interface methods
+            public <T extends ConstraintValidator<?, ?>> T getInstance(Class<T> key) {
+                return null; // always return null
+            }
+        };
+        
+        // Create a validator with this factory
+        ApacheValidatorConfiguration customConfig = Validation.byProvider(ApacheValidationProvider.class).configure().constraintValidatorFactory(customFactory);
+        Validator validator = customConfig.buildValidatorFactory().getValidator();
+        
+        try {
+            validator.validate(new Person());
+            fail("ValidationException must be thrown when factory returns a null constraint validator.");
+        } catch (ValidationException e) {
+            // correct
+        }
+    }
+    
+    @GoodPerson
+    public static class Person {
+    }
+    
+    @Constraint(validatedBy = { GoodPersonValidator.class })
+    @Target({ METHOD, FIELD, ANNOTATION_TYPE, TYPE })
+    @Retention(RUNTIME)
+    @Documented
+    public static @interface GoodPerson {
+        
+        String message() default "Not a good person";
+        Class<?>[] groups() default { };
+        Class<? extends Payload>[] payload() default {};
+        
+        public static class GoodPersonValidator implements ConstraintValidator<GoodPerson, Person> {
+            // @Override - not allowed in 1.5 for Interface methods
+            public void initialize(GoodPerson constraintAnnotation) {
+            }
+
+            // @Override - not allowed in 1.5 for Interface methods
+            public boolean isValid(Person value, ConstraintValidatorContext context) {
+                return true;
+            }
+        }
+    }
+    
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CustomValidatorFactoryTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CustomValidatorFactoryTest.java
new file mode 100644
index 0000000..d2fc38d
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/CustomValidatorFactoryTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.bval.jsr303;
+
+import junit.framework.TestCase;
+
+import javax.validation.*;
+import javax.validation.spi.ConfigurationState;
+
+/**
+ * Test the ability to force a particular {@link ValidatorFactory}
+ * implementation class.
+ * 
+ * @version $Rev$ $Date$
+ */
+public class CustomValidatorFactoryTest extends TestCase {
+
+    public static class CustomValidatorFactory extends ApacheValidatorFactory {
+
+        /**
+         * Create a new CustomValidatorFactory instance.
+         * 
+         * @param configurationState
+         */
+        public CustomValidatorFactory(ConfigurationState configurationState) {
+            super(configurationState);
+        }
+    }
+
+    public static class IncompatibleValidatorFactory implements ValidatorFactory {
+
+        public ConstraintValidatorFactory getConstraintValidatorFactory() {
+            return null;
+        }
+
+        public MessageInterpolator getMessageInterpolator() {
+            return null;
+        }
+
+        public TraversableResolver getTraversableResolver() {
+            return null;
+        }
+
+        public Validator getValidator() {
+            return null;
+        }
+
+        public <T> T unwrap(Class<T> type) {
+            return null;
+        }
+
+        public ValidatorContext usingContext() {
+            return null;
+        }
+
+    }
+
+    public static class NotAValidatorFactory {
+        public NotAValidatorFactory(ConfigurationState configurationState) {
+        }
+    }
+
+    public void testDefaultValidatorFactory() {
+        Validation.byProvider(ApacheValidationProvider.class).configure().buildValidatorFactory().unwrap(
+            ApacheValidatorFactory.class);
+    }
+
+    public void testNoSuchType() {
+        try {
+            Validation.byProvider(ApacheValidationProvider.class).configure().addProperty(
+                ApacheValidatorConfiguration.Properties.VALIDATOR_FACTORY_CLASSNAME, "no.such.type")
+                .buildValidatorFactory();
+            fail();
+        } catch (ValidationException ex) {
+            assertTrue(ex.getCause() instanceof ClassNotFoundException);
+        }
+    }
+
+    public void testCustomValidatorFactory() {
+        doTest(CustomValidatorFactory.class, null);
+    }
+
+    public void testInvalidType() {
+        doTest(NotAValidatorFactory.class, ClassCastException.class);
+    }
+
+    public void testUnsupportedValidatorFactoryType() {
+        doTest(IncompatibleValidatorFactory.class, NoSuchMethodException.class);
+    }
+
+    private void doTest(Class<?> validatorFactoryType, Class<? extends Exception> expectedFailureCause) {
+        try {
+            Validation.byProvider(ApacheValidationProvider.class).configure().addProperty(
+                ApacheValidatorConfiguration.Properties.VALIDATOR_FACTORY_CLASSNAME, validatorFactoryType.getName())
+                .buildValidatorFactory().unwrap(validatorFactoryType);
+            assertNull(expectedFailureCause);
+        } catch (ValidationException ex) {
+            assertNotNull(expectedFailureCause);
+            assertTrue(expectedFailureCause.isInstance(ex.getCause()));
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/DefaultMessageInterpolatorTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/DefaultMessageInterpolatorTest.java
new file mode 100644
index 0000000..369ce09
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/DefaultMessageInterpolatorTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.bval.jsr303;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.jsr303.example.Author;
+import org.apache.bval.jsr303.example.PreferredGuest;
+
+import javax.validation.MessageInterpolator;
+import javax.validation.Validator;
+import javax.validation.constraints.Pattern;
+import javax.validation.metadata.ConstraintDescriptor;
+import java.util.Locale;
+
+/**
+ * MessageResolverImpl Tester.
+ */
+public class DefaultMessageInterpolatorTest extends TestCase {
+
+    private DefaultMessageInterpolator interpolator;
+
+    public DefaultMessageInterpolatorTest(String name) {
+        super(name);
+    }
+
+    public static Test suite() {
+        return new TestSuite(DefaultMessageInterpolatorTest.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp(); // call super!
+        interpolator = new DefaultMessageInterpolator();
+        interpolator.setLocale(Locale.ENGLISH);
+    }
+
+    public void testCreateResolver() {
+
+        final Validator gvalidator = getValidator();
+
+        assertTrue(!gvalidator.getConstraintsForClass(PreferredGuest.class).getConstraintsForProperty(
+            "guestCreditCardNumber").getConstraintDescriptors().isEmpty());
+
+        MessageInterpolator.Context ctx = new MessageInterpolator.Context() {
+
+            public ConstraintDescriptor<?> getConstraintDescriptor() {
+                return (ConstraintDescriptor<?>) gvalidator.getConstraintsForClass(PreferredGuest.class)
+                    .getConstraintsForProperty("guestCreditCardNumber").getConstraintDescriptors().iterator().next();
+            }
+
+            public Object getValidatedValue() {
+                return "12345678";
+            }
+        };
+        String msg = interpolator.interpolate("{validator.creditcard}", ctx);
+        Assert.assertEquals("credit card is not valid", msg);
+
+        ctx = new MessageInterpolator.Context() {
+            public ConstraintDescriptor<?> getConstraintDescriptor() {
+                return gvalidator.getConstraintsForClass(Author.class).getConstraintsForProperty("lastName")
+                    .getConstraintDescriptors().iterator().next();
+            }
+
+            public Object getValidatedValue() {
+                return "";
+            }
+        };
+
+        msg = interpolator.interpolate("{org.apache.bval.constraints.NotEmpty.message}", ctx);
+        Assert.assertEquals("may not be empty", msg);
+    }
+
+    /**
+     * Checks that strings containing special characters are correctly
+     * substituted when interpolating.
+     */
+    public void testReplacementWithSpecialChars() {
+
+        final Validator validator = getValidator();
+        MessageInterpolator.Context ctx;
+
+        // Try to interpolate an annotation attribute containing $
+        ctx = new MessageInterpolator.Context() {
+
+            public ConstraintDescriptor<?> getConstraintDescriptor() {
+                return (ConstraintDescriptor<?>) validator.getConstraintsForClass(Person.class)
+                    .getConstraintsForProperty("idNumber").getConstraintDescriptors().iterator().next();
+            }
+
+            public Object getValidatedValue() {
+                return "12345678";
+            }
+        };
+
+        String result = this.interpolator.interpolate("Id number should match {regexp}", ctx);
+        Assert.assertEquals("Incorrect message interpolation when $ is in an attribute",
+            "Id number should match ....$", result);
+
+        // Try to interpolate an annotation attribute containing \
+        ctx = new MessageInterpolator.Context() {
+
+            public ConstraintDescriptor<?> getConstraintDescriptor() {
+                return (ConstraintDescriptor<?>) validator.getConstraintsForClass(Person.class)
+                    .getConstraintsForProperty("otherId").getConstraintDescriptors().iterator().next();
+            }
+
+            public Object getValidatedValue() {
+                return "12345678";
+            }
+        };
+
+        result = this.interpolator.interpolate("Other id should match {regexp}", ctx);
+        Assert.assertEquals("Incorrect message interpolation when \\ is in an attribute value",
+            "Other id should match .\\n", result);
+
+    }
+
+    public static class Person {
+
+        @Pattern(message = "Id number should match {regexp}", regexp = "....$")
+        public String idNumber;
+
+        @Pattern(message = "Other id should match {regexp}", regexp = ".\\n")
+        public String otherId;
+
+    }
+
+    private Validator getValidator() {
+        return ApacheValidatorFactory.getDefault().getValidator();
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ExceptionsContractTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ExceptionsContractTest.java
new file mode 100644
index 0000000..302fc07
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ExceptionsContractTest.java
@@ -0,0 +1,270 @@
+/*
+ * 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.bval.jsr303;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import javax.validation.Validation;
+import javax.validation.ValidationException;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.metadata.BeanDescriptor;
+import java.util.Locale;
+
+/**
+ * Several checks to validate that the implementations of {@link Validator} and
+ * {@link BeanDescriptor} throw the correct exceptions as per the spec.
+ * 
+ * @author Carlos Vara
+ */
+public class ExceptionsContractTest extends TestCase {
+    static ValidatorFactory factory;
+
+    static {
+        factory = Validation.buildDefaultValidatorFactory();
+        ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+    }
+
+    /**
+     * Validator instance to test
+     */
+    protected Validator validator;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        validator = createValidator();
+    }
+
+    /**
+     * Create the validator instance.
+     * 
+     * @return Validator
+     */
+    protected Validator createValidator() {
+        return factory.getValidator();
+    }
+
+    /**
+     * Checks that the correct exception is thrown when validating a bean whose
+     * getter throws an exception.
+     */
+    public void testExceptionThrowingBean() {
+        try {
+            validator.validate(new ExceptionThrowingBean());
+            Assert.fail("No exception thrown when validating a bean whose getter throws a RTE");
+        } catch (ValidationException e) {
+            // Correct
+        }
+    }
+
+    /**
+     * Checks that an {@link IllegalArgumentException} is thrown when passing
+     * <code>null</code> as group array.
+     */
+    public void testValidateNullGroup() {
+        try {
+            Class<?>[] groups = null;
+            validator.validate(new String(), groups);
+            Assert.fail("No exception thrown when passing null as group array");
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+    }
+
+    /**
+     * Checks that an {@link IllegalArgumentException} is thrown when passing an
+     * invalid property name.
+     */
+    public void testValidateInvalidPropertyName() {
+
+        // Null propertyName
+        try {
+            validator.validateProperty(new Person(), null);
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+
+        // Empty propertyName
+        try {
+            validator.validateProperty(new Person(), "");
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+
+        // Invalid propertyName
+        try {
+            validator.validateProperty(new Person(), "surname");
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+
+    }
+
+    /**
+     * Checks that an {@link IllegalArgumentException} is thrown when trying to
+     * validate a property on a null object.
+     */
+    public void testValidatePropertyOnNullBean() {
+        try {
+            validator.validateProperty(null, "class");
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+    }
+
+    /**
+     * Checks that an {@link IllegalArgumentException} is thrown when passing
+     * <code>null</code> as group array in a
+     * {@link Validator#validateProperty(Object, String, Class...)} call.
+     */
+    public void testValidatePropertyNullGroup() {
+        try {
+            Class<?>[] groups = null;
+            validator.validateProperty(new Person(), "name", groups);
+            Assert.fail("No exception thrown when passing null as group array");
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+    }
+
+    /**
+     * Checks that an {@link IllegalArgumentException} is thrown when calling
+     * {@link Validator#validateValue(Class, String, Object, Class...)} with a
+     * <code>null</code> class.
+     */
+    public void testValidateValueOnNullClass() {
+        try {
+            validator.validateValue(null, "class", Object.class);
+            Assert.fail("No exception thrown when passing null as group array");
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+    }
+
+    /**
+     * Checks that an {@link IllegalArgumentException} is thrown when passing an
+     * invalid property name to
+     * {@link Validator#validateValue(Class, String, Object, Class...)}.
+     */
+    public void testValidateValueInvalidPropertyName() {
+        // Null propertyName
+        try {
+            validator.validateValue(Person.class, null, "John");
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+
+        // Empty propertyName
+        try {
+            validator.validateValue(Person.class, "", "John");
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+
+        // Invalid propertyName
+        try {
+            validator.validateValue(Person.class, "unexistant", "John");
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+    }
+
+    /**
+     * Checks that an {@link IllegalArgumentException} is thrown when calling
+     * {@link Validator#validateValue(Class, String, Object, Class...)} with a
+     * <code>null</code> group array.
+     */
+    public void testValidateValueNullGroup() {
+        try {
+            Class<?>[] groups = null;
+            validator.validateValue(Person.class, "name", "John", groups);
+            Assert.fail("No exception thrown when passing null as group array");
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+    }
+
+    /**
+     * Enforces the "not a valid object property" part of the {@link IllegalArgumentException}
+     * declaration on {@link Validator#validateValue(Class, String, Object, Class...)}
+     */
+    public void testValidateIncompatibleValue() {
+        try {
+            validator.validateValue(Person.class, "name", 666);
+            Assert.fail("No exception thrown when passing Integer for string value");
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+        try {
+            validator.validateValue(Person.class, "age", null);
+            Assert.fail("No exception thrown when passing null for primitive value");
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+    }
+
+    /**
+     * Checks that an {@link IllegalArgumentException} is thrown when calling
+     * {@link BeanDescriptor#getConstraintsForProperty(String)} with an invalid
+     * property name.
+     */
+    public void testGetConstraintsForInvalidProperty() {
+        BeanDescriptor personDescriptor = validator.getConstraintsForClass(Person.class);
+
+        try {
+            personDescriptor.getConstraintsForProperty(null);
+            fail("No exception thrown when calling getConstraintsForProperty with null property");
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+
+        try {
+            personDescriptor.getConstraintsForProperty("");
+            fail("No exception thrown when calling getConstraintsForProperty with empty property");
+        } catch (IllegalArgumentException e) {
+            // Correct
+        }
+    }
+
+    public static class ExceptionThrowingBean {
+
+        @NotNull
+        public String getValue() {
+            throw new IllegalStateException();
+        }
+
+    }
+
+    public static class Person {
+
+        @NotNull
+        public String name;
+
+        @Min(0)
+        public int age;
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/FooTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/FooTest.java
new file mode 100644
index 0000000..359552c
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/FooTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.bval.jsr303;
+
+import junit.framework.TestCase;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Valid;
+import javax.validation.Validator;
+import javax.validation.constraints.NotNull;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Description: <br/>
+ */
+public class FooTest extends TestCase {
+
+    @Valid
+    private Collection<Foo> foos = new ArrayList<Foo>();
+
+    public FooTest() {
+        foos.add(new Foo("foo1"));
+        foos.add(null);
+        foos.add(new Foo("foo3"));
+    }
+
+
+    public class Foo {
+        @NotNull
+        public String bar;
+
+        public Foo(String bar) {
+            this.bar = bar;
+        }
+
+    }
+
+    public void testValidation() {
+        FooTest t = new FooTest();
+
+        Validator v = ApacheValidatorFactory.getDefault().getValidator();
+        Set<ConstraintViolation<FooTest>> errors = v.validate(t);
+        System.out.println("got errors:");
+        for (ConstraintViolation<?> error : errors) {
+            System.out.println(error.getPropertyPath());
+        }
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/Jsr303Test.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/Jsr303Test.java
new file mode 100644
index 0000000..9dbd117
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/Jsr303Test.java
@@ -0,0 +1,273 @@
+/*
+ * 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.bval.jsr303;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.UnexpectedTypeException;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor;
+import javax.validation.metadata.PropertyDescriptor;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.bval.constraints.SizeValidatorForString;
+import org.apache.bval.jsr303.example.Address;
+import org.apache.bval.jsr303.example.Book;
+import org.apache.bval.jsr303.example.Engine;
+import org.apache.bval.jsr303.example.IllustratedBook;
+import org.apache.bval.jsr303.example.MaxTestEntity;
+import org.apache.bval.jsr303.example.NoValidatorTestEntity;
+import org.apache.bval.jsr303.example.Second;
+import org.apache.bval.jsr303.example.SizeTestEntity;
+import org.apache.bval.jsr303.util.TestUtils;
+
+/**
+ * Description: <br/>
+ */
+public class Jsr303Test extends TestCase {
+    /*
+     * static { ApacheValidatorFactory.getDefault().getMetaBeanManager()
+     * .addResourceLoader("org/apache/bval/example/test-beanInfos.xml"); }
+     */
+
+    /*
+     * public void testUseCoreXmlMetaData() { Validator validator =
+     * getValidator();
+     * 
+     * BusinessObject object = new BusinessObject();
+     * object.setTitle("1234567834567 too long title ");
+     * Set<ConstraintViolation<BusinessObject>> violations =
+     * validator.validate(object); Assert.assertNotNull(violations);
+     * Assert.assertTrue(!violations.isEmpty());
+     * 
+     * Assert.assertTrue(!validator.validateProperty(object,
+     * "title").isEmpty()); }
+     */
+
+    static ValidatorFactory factory;
+
+    static {
+        factory = Validation.buildDefaultValidatorFactory();
+        ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+    }
+
+    /**
+     * Validator instance to test
+     */
+    protected Validator validator;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        validator = createValidator();
+    }
+
+    /**
+     * Create the validator instance.
+     * 
+     * @return Validator
+     */
+    protected Validator createValidator() {
+        return factory.getValidator();
+    }
+
+    public void testPropertyDescriptorHasConstraints() {
+        BeanDescriptor cons = validator.getConstraintsForClass(Book.class);
+        assertTrue(cons.getConstraintsForProperty("author").hasConstraints());
+        assertTrue(cons.getConstraintsForProperty("title").hasConstraints());
+        assertTrue(cons.getConstraintsForProperty("uselessField").hasConstraints());
+        // cons.getConstraintsForProperty("unconstraintField") == null without
+        // Introspector
+        // cons.getConstraintsForProperty("unconstraintField") != null with
+        // Introspector
+        assertTrue(cons.getConstraintsForProperty("unconstraintField") == null
+            || !cons.getConstraintsForProperty("unconstraintField").hasConstraints());
+        assertNull(cons.getConstraintsForProperty("unknownField"));
+    }
+
+    public void testValidateValue() {
+        assertTrue(validator.validateValue(Book.class, "subtitle", "123456789098765432").isEmpty());
+        assertFalse(validator.validateValue(Book.class, "subtitle",
+            "123456789098765432123412345678909876543212341234564567890987654321234", Second.class).isEmpty());
+        // tests for issue 22: validation of a field without any constraints
+        assertEquals(0, validator.validateValue(Book.class, "unconstraintField", 4).size());
+        // tests for issue 22: validation of unknown field cause
+        // ValidationException
+        try {
+            validator.validateValue(Book.class, "unknownProperty", 4);
+            fail("unknownProperty not detected");
+        } catch (IllegalArgumentException ex) {
+            // OK
+            assertEquals("unknown property 'unknownProperty' in org.apache.bval.jsr303.example.Book", ex.getMessage());
+        }
+    }
+
+    public void testValidateNonCascadedRealNestedProperty() {
+        try {
+            validator.validateValue(IllustratedBook.class, "illustrator.firstName", "Edgar");
+            fail("unknownProperty not detected");
+        } catch (IllegalArgumentException ex) {
+            // OK
+            assertEquals("Property org.apache.bval.jsr303.example.IllustratedBook.illustrator is not cascaded", ex.getMessage());
+        }
+    }
+
+    public void testMetadataAPI_Book() {
+        Assert.assertNotNull(validator.getConstraintsForClass(Book.class));
+        // not necessary for implementation correctness, but we'll test
+        // nevertheless:
+        Assert.assertSame(validator.getConstraintsForClass(Book.class), validator.getConstraintsForClass(Book.class));
+        BeanDescriptor bc = validator.getConstraintsForClass(Book.class);
+        // assertEquals(ElementType.TYPE, bc.getElementType());
+        Assert.assertEquals(Book.class, bc.getElementClass());
+        // assertEquals(false, bc.isCascaded());
+        // assertEquals("", bc.getPropertyPath());
+        Assert.assertTrue(bc.getConstraintDescriptors() != null);
+        TestUtils.failOnModifiable(bc.getConstraintDescriptors(), "beanDescriptor constraintDescriptors");
+    }
+
+    public void testMetadataAPI_Engine() {
+        ElementDescriptor desc =
+            validator.getConstraintsForClass(Engine.class).getConstraintsForProperty("serialNumber");
+        assertNotNull(desc);
+        // assertEquals(ElementType.FIELD, desc.getElementType());
+        Assert.assertEquals(String.class, desc.getElementClass());
+    }
+
+    public void testMetadataAPI_Address() {
+        Assert.assertFalse(validator.getConstraintsForClass(Address.class).getConstraintDescriptors().isEmpty());
+
+        Set<PropertyDescriptor> props = validator.getConstraintsForClass(Address.class).getConstrainedProperties();
+        TestUtils.failOnModifiable(props, "beanDescriptor constrainedProperties");
+        Set<String> propNames = new HashSet<String>(props.size());
+        for (PropertyDescriptor each : props) {
+            TestUtils.failOnModifiable(each.getConstraintDescriptors(), "propertyDescriptor constraintDescriptors");
+            propNames.add(each.getPropertyName());
+        }
+        Assert.assertTrue(propNames.contains("addressline1")); // annotated at
+        // field level
+        Assert.assertTrue(propNames.contains("addressline2"));
+        Assert.assertTrue(propNames.contains("zipCode"));
+        Assert.assertTrue(propNames.contains("country"));
+        Assert.assertTrue(propNames.contains("city")); // annotated at method
+        // level
+        Assert.assertEquals(5, props.size());
+
+        ElementDescriptor desc =
+            validator.getConstraintsForClass(Address.class).getConstraintsForProperty("addressline1");
+        Assert.assertNotNull(desc);
+        boolean found = false;
+        for (ConstraintDescriptor<?> each : desc.getConstraintDescriptors()) {
+            if (each.getConstraintValidatorClasses().get(0).equals(SizeValidatorForString.class)) {
+                Assert.assertTrue(each.getAttributes().containsKey("max"));
+                assertEquals(30, each.getAttributes().get("max"));
+                found = true;
+            }
+        }
+        Assert.assertTrue(found);
+
+    }
+
+    public void testValidateMultiValuedConstraints() {
+        Engine engine = new Engine();
+        engine.serialNumber = "abcd-defg-0123";
+        Set<ConstraintViolation<Engine>> violations;
+        violations = validator.validate(engine);
+        assertEquals(0, violations.size());
+
+        engine.serialNumber = "!)/(/()";
+        violations = validator.validate(engine);
+        assertEquals(2, violations.size());
+        for (String msg : new String[] { "must contain alphabetical characters only", "must match ....-....-...." }) {
+            assertNotNull(TestUtils.getViolationWithMessage(violations, msg));
+        }
+    }
+
+    public void testConstraintValidatorResolutionAlgorithm() {
+        MaxTestEntity entity = new MaxTestEntity();
+        entity.setText("101");
+        entity.setProperty("201");
+        entity.setLongValue(301);
+        entity.setDecimalValue(new BigDecimal(401));
+        Set<ConstraintViolation<MaxTestEntity>> violations = validator.validate(entity);
+        assertEquals(4, violations.size());
+
+        NoValidatorTestEntity entity2 = new NoValidatorTestEntity();
+        try {
+            validator.validate(entity2);
+            fail("UnexpectedTypeException expected but not thrown");
+        } catch (UnexpectedTypeException ex) {
+            // we expected this
+            assertEquals("No validator could be found for type java.lang.Object. "
+                + "See: @Max at private java.lang.Object " + "org.apache.bval.jsr303.example."
+                + "NoValidatorTestEntity.anything", ex.getMessage());
+        }
+    }
+
+    public void testSizeValidation() {
+        SizeTestEntity en = new SizeTestEntity();
+        en.ba = new byte[3];
+        en.ca = new char[3];
+        en.boa = new boolean[3];
+        en.coll = Arrays.asList("1", "2", "3");
+        en.da = new double[3];
+        en.fa = new float[3];
+        en.it = new int[3];
+        en.la = new long[3];
+        en.map = new HashMap<String, String>();
+        en.map.put("1", "1");
+        en.map.put("3", "3");
+        en.map.put("2", "2");
+        en.oa = new Integer[3];
+        en.oa2 = new Integer[3];
+        en.sa = new short[3];
+        en.text = "123";
+        Set<ConstraintViolation<SizeTestEntity>> vi = validator.validate(en);
+        assertEquals(13, vi.size());
+    }
+
+    /**
+     * JSR-303 Section 5.1.c, IllegalArgumentException should be thrown
+     */
+    public void testGetConstraintsForNullClass() {
+        try {
+            validator.getConstraintsForClass(null);
+            Assert.fail("No exception thrown on Validator.getConstraintsForClass(null)");
+        } catch (IllegalArgumentException e) {
+            // Correct
+            return;
+        }
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/PayloadTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/PayloadTest.java
new file mode 100644
index 0000000..54fa661
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/PayloadTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.bval.jsr303;
+
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Payload;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.NotNull;
+
+import junit.framework.TestCase;
+
+import org.apache.bval.jsr303.util.TestUtils;
+
+/**
+ * Description: test that payload information can be retrieved
+ * from error reports via the ConstraintDescriptor either accessed
+ * through the ConstraintViolation objects<br/>
+ */
+public class PayloadTest extends TestCase {
+    static ValidatorFactory factory;
+
+    static {
+        factory = Validation.buildDefaultValidatorFactory();
+        ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+    }
+
+    /**
+     * Validator instance to test
+     */
+    protected Validator validator;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        validator = createValidator();
+    }
+
+    /**
+     * Create the validator instance.
+     * 
+     * @return Validator
+     */
+    protected Validator createValidator() {
+        return factory.getValidator();
+    }
+
+    static class Severity {
+        static class Info implements Payload {
+        }
+
+        static class Error implements Payload {
+        }
+    }
+
+    static class Address {
+        private String zipCode;
+        private String city;
+
+        Address(String zipCode, String city) {
+            this.zipCode = zipCode;
+            this.city = city;
+        }
+
+        @NotNull(message = "would be nice if we had one", payload = Severity.Info.class)
+        public String getZipCode() {
+            return zipCode;
+        }
+
+        @NotNull(message = "the city is mandatory", payload = Severity.Error.class)
+        public String getCity() {
+            return city;
+        }
+    }
+
+    public void testPayload() {
+        Set<ConstraintViolation<Address>> violations;
+        Address address = new Address(null, null);
+        violations = validator.validate(address);
+        assertEquals(2, violations.size());
+        ConstraintViolation<?> vio;
+        vio = TestUtils.getViolation(violations, "zipCode");
+        assertNotNull(vio);
+        assertEquals(1, vio.getConstraintDescriptor().getPayload().size());
+        assertTrue(
+              vio.getConstraintDescriptor().getPayload().contains(Severity.Info.class));
+
+        vio = TestUtils.getViolation(violations, "city");
+        assertNotNull(vio);
+        assertEquals(1, vio.getConstraintDescriptor().getPayload().size());
+        assertTrue(
+              vio.getConstraintDescriptor().getPayload().contains(Severity.Error.class));
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/TckReproducerTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/TckReproducerTest.java
new file mode 100644
index 0000000..92d9806
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/TckReproducerTest.java
@@ -0,0 +1,115 @@
+/*
+ *  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.bval.jsr303;
+
+import junit.framework.TestCase;
+import org.apache.bval.util.PropertyAccess;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.Pattern;
+
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Description: <br>
+ * User: roman.stumm<br>
+ * Date: 21.04.2010<br>
+ * Time: 14:21:45<br>
+ */
+public class TckReproducerTest extends TestCase {
+    static ValidatorFactory factory;
+
+    static {
+        factory = Validation.buildDefaultValidatorFactory();
+        ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+    }
+
+    /**
+     * Validator instance to test
+     */
+    protected Validator validator;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        validator = createValidator();
+    }
+
+    /**
+     * Create the validator instance.
+     * 
+     * @return Validator
+     */
+    protected Validator createValidator() {
+        return factory.getValidator();
+    }
+
+    public static <T> void assertCorrectNumberOfViolations(Set<ConstraintViolation<T>> violations,
+        int expectedViolations) {
+        assertEquals("Wrong number of constraint violations. Expected: " + expectedViolations + " Actual: "
+            + violations.size(), expectedViolations, violations.size());
+    }
+
+    public void testPropertyAccessOnNonPublicClass() throws Exception {
+        Car car = new Car("USd-298");
+        assertEquals(car.getLicensePlateNumber(), PropertyAccess.getProperty(car, "licensePlateNumber"));
+
+        Set<ConstraintViolation<Car>> violations =
+            validator.validateProperty(car, "licensePlateNumber", First.class,
+                org.apache.bval.jsr303.example.Second.class);
+        assertCorrectNumberOfViolations(violations, 1);
+
+        car.setLicensePlateNumber("USD-298");
+        violations =
+            validator.validateProperty(car, "licensePlateNumber", First.class,
+                org.apache.bval.jsr303.example.Second.class);
+        assertCorrectNumberOfViolations(violations, 0);
+    }
+
+    class Car {
+        @Pattern(regexp = "[A-Z][A-Z][A-Z]-[0-9][0-9][0-9]", groups = { First.class, Second.class })
+        private String licensePlateNumber;
+
+        Car(String licensePlateNumber) {
+            this.licensePlateNumber = licensePlateNumber;
+        }
+
+        public String getLicensePlateNumber() {
+            return licensePlateNumber;
+        }
+
+        public void setLicensePlateNumber(String licensePlateNumber) {
+            this.licensePlateNumber = licensePlateNumber;
+        }
+    }
+
+    interface First {
+    }
+
+    interface Second {
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidationTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidationTest.java
new file mode 100644
index 0000000..8f571fb
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidationTest.java
@@ -0,0 +1,722 @@
+/*
+ * 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.bval.jsr303;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import javax.validation.groups.Default;
+import javax.validation.metadata.BeanDescriptor;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.PropertyDescriptor;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.bval.constraints.NotNullValidator;
+import org.apache.bval.jsr303.example.AccessTestBusinessObject;
+import org.apache.bval.jsr303.example.AccessTestBusinessObjectSub;
+import org.apache.bval.jsr303.example.Address;
+import org.apache.bval.jsr303.example.Author;
+import org.apache.bval.jsr303.example.Book;
+import org.apache.bval.jsr303.example.BusinessAddress;
+import org.apache.bval.jsr303.example.Continent;
+import org.apache.bval.jsr303.example.Country;
+import org.apache.bval.jsr303.example.First;
+import org.apache.bval.jsr303.example.Last;
+import org.apache.bval.jsr303.example.RecursiveFoo;
+import org.apache.bval.jsr303.util.TestUtils;
+
+/**
+ * Description: <br/>
+ */
+public class ValidationTest extends TestCase {
+    static ValidatorFactory factory;
+
+    static {
+        factory = Validation.buildDefaultValidatorFactory();
+        ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+    }
+
+    /**
+     * Validator instance to test
+     */
+    protected Validator validator;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        validator = createValidator();
+    }
+
+    /**
+     * Create the validator instance.
+     * 
+     * @return Validator
+     */
+    protected Validator createValidator() {
+        return factory.getValidator();
+    }
+
+    public void testAccessStrategies_field_method() {
+        AccessTestBusinessObject o1 = new AccessTestBusinessObject("1");
+        AccessTestBusinessObjectSub o2 = new AccessTestBusinessObjectSub("3");
+        Set<ConstraintViolation<AccessTestBusinessObject>> errors = validator.validate(o1);
+        assertTrue(errors.isEmpty());
+        Set<ConstraintViolation<AccessTestBusinessObjectSub>> errors2 = validator.validate(o2);
+        assertTrue(errors2.isEmpty());
+
+        o2 = new AccessTestBusinessObjectSub("1");
+        errors2 = validator.validate(o2);
+        assertEquals(1, errors2.size());
+
+        // assert, that getvar2() and getVar2() are both validated with their
+        // getter method
+        o2 = new AccessTestBusinessObjectSub("3");
+        o2.setVar2("1");
+        o2.setvar2("2");
+        errors2 = validator.validate(o2);
+        assertEquals(2, errors2.size());
+
+        o2.setvar2("5");
+        o2.setVar2("6");
+        errors2 = validator.validate(o2);
+        assertEquals(0, errors2.size());
+
+        o2.setvar2("5");
+        o2.setVar2("-1");
+        errors2 = validator.validate(o2);
+        assertEquals(1, errors2.size());
+    }
+
+    public void testAccessStrategies_on_children() {
+        AccessTestBusinessObject o1 = new AccessTestBusinessObject("1");
+        AccessTestBusinessObject o2 = new AccessTestBusinessObject("2");
+        o1.next(o2);
+        Set<ConstraintViolation<AccessTestBusinessObject>> errors = validator.validate(o1);
+        // assert, that field access 'next' is used and not getNext() is
+        // called!!!
+        assertEquals(1, errors.size());
+        o2 = new AccessTestBusinessObject("1");
+        o1.next(o2);
+        errors = validator.validate(o1);
+        assertEquals(0, errors.size());
+
+        // assert that toBeIgnored not validated, because not annotated with
+        // @Valid
+        o1.setToBeIgnored(new AccessTestBusinessObject("99"));
+        errors = validator.validate(o1);
+        assertEquals(0, errors.size());
+
+        o1.setNext(new AccessTestBusinessObject("99"));
+        errors = validator.validate(o1);
+        assertEquals(1, errors.size());
+    }
+
+    public void testBook() {
+        Author author = new Author();
+        author.setLastName("Baudelaire");
+        author.setFirstName("");
+        Book book = new Book();
+        book.setAuthor(author);
+        book.setSubtitle("12345678900125678901234578901234567890");
+
+        // NotEmpty failure on the title field
+        Set<ConstraintViolation<Book>> errors = validator.validate(book, Book.All.class);
+        Assert.assertTrue(!errors.isEmpty());
+
+        book.setTitle("Les fleurs du mal");
+        author.setCompany("Some random publisher with a very very very long name");
+
+        // author.firstName fails to pass the NotEmpty constraint
+        // author.company fails to pass the Size constraint
+    }
+
+    /**
+     * test: - dynamic resolution of associated object types. - inheritance of validation constraints - complex
+     * valiation, different groups, nested object net
+     */
+    public void testValidAnnotation() {
+        Author a = new Author();
+        a.setAddresses(new ArrayList<Address>());
+        BusinessAddress adr = new BusinessAddress();
+        adr.setCountry(new Country());
+        adr.setAddressline1("line1");
+        adr.setAddressline2("line2");
+
+        adr.setZipCode("1234567890123456789");
+        a.getAddresses().add(adr);
+
+        a.setFirstName("Karl");
+        a.setLastName("May");
+
+        Set<ConstraintViolation<Author>> found = validator.validate(a, Default.class, First.class, Last.class);
+        Assert.assertTrue(!found.isEmpty());
+        Assert.assertEquals(4, found.size());
+
+        adr.setCity("Berlin");
+        adr.setZipCode("12345");
+        adr.setCompany("apache");
+        found = validator.validate(a, Default.class, First.class, Last.class);
+        Assert.assertEquals(1, found.size());
+        ConstraintViolation<Author> ic = found.iterator().next();
+        Assert.assertEquals("addresses[0].country.name", ic.getPropertyPath().toString());
+    }
+
+    public void testPropertyPathWithIndex() {
+        Author a = new Author();
+        a.setAddresses(new ArrayList<Address>());
+        Address adr = new Address();
+        adr.setAddressline1("adr1");
+        adr.setCity("Santiago");
+        a.getAddresses().add(adr);
+        adr = new Address();
+        adr.setAddressline1("adr2");
+        adr.setCity("Havanna");
+        a.getAddresses().add(adr);
+        adr = new Address();
+        adr.setAddressline1("adr3");
+        adr.setCity("Trinidad");
+        a.getAddresses().add(adr);
+
+        Set<ConstraintViolation<Author>> constraints = validator.validate(a);
+        Assert.assertTrue(!constraints.isEmpty());
+
+        assertPropertyPath("addresses[0].country", constraints);
+        assertPropertyPath("addresses[1].country", constraints);
+        assertPropertyPath("addresses[2].country", constraints);
+    }
+
+    /**
+     * Check correct path reporting when validating a set of beans.
+     */
+    public void testPropertyPathOnSet() {
+        Continent c = new Continent();
+        c.name = "c1";
+        Country country = new Country();
+        country.setISO2Code("xx");
+        country.setISO3Code("xxx");
+        country.setName(null);
+        c.countries.add(country);
+
+        Set<ConstraintViolation<Continent>> constraints = validator.validate(c);
+        Assert.assertEquals("Incorrect number of violations detected", 1, constraints.size());
+        assertPropertyPath("countries[].name", constraints);
+
+    }
+
+    private <T> void assertPropertyPath(String propertyPath, Set<ConstraintViolation<T>> constraints) {
+        for (ConstraintViolation<T> each : constraints) {
+            if (each.getPropertyPath().toString().equals(propertyPath))
+                return;
+        }
+        Assert.fail(propertyPath + " not found in " + constraints);
+    }
+
+    public void testPropertyPathRecursive() {
+        RecursiveFoo foo1 = new RecursiveFoo(); // root
+        RecursiveFoo foo11 = new RecursiveFoo();
+        foo1.getFoos().add(foo11); // foos[0]
+        RecursiveFoo foo12 = new RecursiveFoo();
+        foo1.getFoos().add(foo12); // foos[1]
+        RecursiveFoo foo2 = new RecursiveFoo();
+        foo11.getFoos().add(foo2); // foos[0].foos[0]
+
+        Set<ConstraintViolation<RecursiveFoo>> constraints = validator.validate(foo1);
+        assertPropertyPath("foos[0].foos[0].foos", constraints);
+        assertPropertyPath("foos[1].foos", constraints);
+    }
+
+    public void testNullElementInCollection() {
+        try {
+            validator.validate(null);
+            Assert.fail();
+        } catch (IllegalArgumentException ex) {
+        }
+        RecursiveFoo foo = new RecursiveFoo();
+        foo.getFoos().add(new RecursiveFoo());
+        foo.getFoos().add(null);
+        Assert.assertTrue(!validator.validate(foo).isEmpty());
+        // check that no nullpointer exception gets thrown
+    }
+
+    public void testGroups() {
+        Author author = new Author();
+        author.setCompany("ACME");
+        Book book = new Book();
+        book.setTitle("");
+        book.setAuthor(author);
+        boolean foundTitleConstraint = false;
+        Set<ConstraintViolation<Book>> constraintViolations = validator.validate(book, Book.All.class);
+        assertEquals(1, constraintViolations.size());
+        // assuming an english locale, the interpolated message is returned
+        for (ConstraintViolation<Book> constraintViolation : constraintViolations) {
+            if (constraintViolation.getRootBean().getClass() == Book.class) {
+                Assert.assertEquals("may not be empty", constraintViolation.getMessage());
+                Assert.assertTrue(book == constraintViolation.getRootBean());
+
+                // the offending property
+                if (constraintViolation.getPropertyPath().toString().equals("title")) {
+                    foundTitleConstraint = true;
+                    // the offending value
+                    Assert.assertEquals(book.getTitle(), constraintViolation.getInvalidValue());
+                }
+            }
+        }
+        Assert.assertTrue(foundTitleConstraint);
+    }
+
+    /**
+     * Example 2.14. Using the fluent API to build custom constraint violations. test that: the
+     * {@link org.apache.bval.constraints.ZipCodeCityCoherenceValidator} adds custom messages to the context and
+     * suppresses the default message
+     */
+    public void testConstraintValidatorContextFluentAPI() {
+        Address ad = new Address();
+        ad.setCity("error");
+        ad.setZipCode("error");
+        ad.setAddressline1("something");
+        ad.setCountry(new Country());
+        ad.getCountry().setName("something");
+        Set<ConstraintViolation<Address>> violations = validator.validate(ad);
+        Assert.assertEquals(2, violations.size());
+        for (ConstraintViolation<Address> each : violations) {
+            Assert.assertTrue(each.getMessage().endsWith(" not OK"));
+        }
+        assertNotNull(TestUtils.getViolation(violations, "city"));
+        assertNotNull(TestUtils.getViolation(violations, ""));
+    }
+
+    public void testValidateNestedPropertyPath() throws InvocationTargetException, NoSuchMethodException,
+        IllegalAccessException {
+        final String propPath = "addresses[0].country.ISO2Code";
+
+        Author author = new Author();
+        author.setAddresses(new ArrayList<Address>());
+        Address adr = new Address();
+        author.getAddresses().add(adr);
+        Country country = new Country();
+        adr.setCountry(country);
+        country.setISO2Code("too_long");
+
+        Set<ConstraintViolation<Author>> iv = validator.validateProperty(author, propPath);
+        Assert.assertEquals(1, iv.size());
+        ConstraintViolation<Author> vio = iv.iterator().next();
+        assertEquals(propPath, vio.getPropertyPath().toString());
+        assertSame(author, vio.getRootBean());
+        assertSame(author.getAddresses().get(0).getCountry(), vio.getLeafBean());
+
+        country.setISO2Code("23");
+        iv = validator.validateProperty(author, propPath);
+        Assert.assertEquals(0, iv.size());
+
+        iv = validator.validateValue(Author.class, propPath, "345");
+        Assert.assertEquals(1, iv.size());
+        vio = iv.iterator().next();
+        assertEquals(propPath, vio.getPropertyPath().toString());
+        assertNull(vio.getRootBean());
+        assertNull(vio.getLeafBean());
+
+        iv = validator.validateValue(Author.class, propPath, "34");
+        Assert.assertEquals(0, iv.size());
+    }
+
+    public void testValidateCascadingNestedBean() throws InvocationTargetException, NoSuchMethodException,
+        IllegalAccessException {
+        final String propPath = "addresses[0]";
+
+        CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+        Author author = new Author();
+        author.setAddresses(new ArrayList<Address>());
+        Address adr = new Address();
+        author.getAddresses().add(adr);
+        Country country = new Country();
+        adr.setCity("dark");
+        adr.setCountry(country);
+
+        Set<ConstraintViolation<Author>> iv = v.validateProperty(author, propPath);
+        Assert.assertEquals(1, iv.size()); // null address line 1 (no cascade)
+
+        country.setISO2Code("too_long");
+        iv = v.validateProperty(author, propPath, true);
+        Assert.assertEquals(3, iv.size()); // null address line 1 + null
+        // country.name + too long
+        // country.iso2code
+
+        country.setISO2Code("23");
+        iv = v.validateProperty(author, propPath, true);
+        Assert.assertEquals(2, iv.size()); // null address line 1 + null
+        // country.name, country.iso2code
+        // fixed
+
+        Address value = new Address();
+        value.setCity("whatever");
+        value.setAddressline1("1 address line");
+        iv = v.validateValue(Author.class, propPath, value, true);
+        Assert.assertEquals(1, iv.size()); // null country
+
+        value.setCountry(new Country());
+        iv = v.validateValue(Author.class, propPath, value, true);
+        Assert.assertEquals(1, iv.size()); // null country.name
+
+        value.getCountry().setName("NWO");
+        iv = v.validateValue(Author.class, propPath, value, true);
+        Assert.assertEquals(0, iv.size());
+    }
+
+    public void testValidateCascadingNestedProperty() throws InvocationTargetException, NoSuchMethodException,
+        IllegalAccessException {
+        final String propPath = "addresses[0].country";
+
+        CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+        Author author = new Author();
+        author.setAddresses(new ArrayList<Address>());
+        Address adr = new Address();
+        author.getAddresses().add(adr);
+        Country country = new Country();
+        adr.setCity("dark");
+        adr.setCountry(country);
+
+        Set<ConstraintViolation<Author>> iv = v.validateProperty(author, propPath);
+        Assert.assertEquals(0, iv.size());
+
+        country.setISO2Code("too_long");
+        iv = v.validateProperty(author, propPath, true);
+        Assert.assertEquals(2, iv.size());
+        // country.name + too long
+        // country.iso2code
+
+        country.setISO2Code("23");
+        iv = v.validateProperty(author, propPath, true);
+        Assert.assertEquals(1, iv.size());
+        // country.name, country.iso2code
+
+        Country value = null;
+        iv = v.validateValue(Author.class, propPath, value, true);
+        Assert.assertEquals(1, iv.size()); // null country
+
+        value = new Country();
+        iv = v.validateValue(Author.class, propPath, value, true);
+        Assert.assertEquals(1, iv.size()); // null country.name
+
+        value.setName("NWO");
+        iv = v.validateValue(Author.class, propPath, value, true);
+        Assert.assertEquals(0, iv.size());
+    }
+
+    public void testValidateCascadingNestedTipProperty() {
+        final String propPath = "addresses[0].country.name";
+
+        CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+        Author author = new Author();
+        author.setAddresses(new ArrayList<Address>());
+        Address adr = new Address();
+        author.getAddresses().add(adr);
+        Country country = new Country();
+        adr.setCity("dark");
+        adr.setCountry(country);
+
+        Set<ConstraintViolation<Author>> iv = v.validateProperty(author, propPath);
+        Assert.assertEquals(1, iv.size());
+
+        iv = v.validateProperty(author, propPath, true);
+        Assert.assertEquals(1, iv.size());
+    }
+
+    public void testValidateCascadingKeyedElement() throws InvocationTargetException, NoSuchMethodException,
+        IllegalAccessException {
+        final String propPath = "[foo]";
+
+        CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+        final Address adr = new Address();
+        @SuppressWarnings("serial")
+        Object map = new HashMap<String, Address>() {
+            {
+                put("foo", adr);
+            }
+        };
+        Country country = new Country();
+        adr.setCity("dark");
+        adr.setCountry(country);
+        Set<ConstraintViolation<Object>> iv = v.validateProperty(map, propPath);
+        Assert.assertEquals(1, iv.size()); // null address line 1 (no cascade)
+
+        country.setISO2Code("too_long");
+        iv = v.validateProperty(map, propPath, true);
+        Assert.assertEquals(3, iv.size()); // null address line 1 + null
+        // country.name + too long
+        // country.iso2code
+
+        country.setISO2Code("23");
+        iv = v.validateProperty(map, propPath, true);
+        Assert.assertEquals(2, iv.size()); // null address line 1 + null
+        // country.name, country.iso2code
+        // fixed
+
+        Address value = new Address();
+        value.setCity("whatever");
+        value.setAddressline1("1 address line");
+
+        Set<?> iv2 = v.validateValue(map.getClass(), propPath, value, true);
+        Assert.assertEquals(1, iv2.size()); // null country
+
+        value.setCountry(new Country());
+        iv2 = v.validateValue(map.getClass(), propPath, value, true);
+        Assert.assertEquals(1, iv2.size()); // null country.name
+
+        value.getCountry().setName("NWO");
+        iv2 = v.validateValue(map.getClass(), propPath, value, true);
+        Assert.assertEquals(0, iv2.size());
+    }
+
+    @SuppressWarnings("unchecked")
+    public void testValidateCascadingKeyedGenericElement() throws InvocationTargetException, NoSuchMethodException,
+        IllegalAccessException {
+        final String propPath = "[foo]";
+
+        CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+        final Address adr = new Address();
+        Object map = new HashMap<String, Address>();
+        ((Map<String, Address>) map).put("foo", adr);
+        Country country = new Country();
+        adr.setCity("dark");
+        adr.setCountry(country);
+        Set<?> iv = v.validateProperty(map, propPath);
+        Assert.assertEquals(1, iv.size()); // null address line 1 (no cascade)
+
+        country.setISO2Code("too_long");
+        iv = v.validateProperty(map, propPath, true);
+        Assert.assertEquals(3, iv.size()); // null address line 1 + null
+        // country.name + too long
+        // country.iso2code
+
+        country.setISO2Code("23");
+        iv = v.validateProperty(map, propPath, true);
+        Assert.assertEquals(2, iv.size()); // null address line 1 + null
+        // country.name, country.iso2code
+        // fixed
+
+        Address value = new Address();
+        value.setCity("whatever");
+        value.setAddressline1("1 address line");
+
+        Set<?> iv2 = v.validateValue(Map.class, propPath, value, true);
+        Assert.assertEquals(1, iv2.size()); // null country
+
+        value.setCountry(new Country());
+        iv2 = v.validateValue(Map.class, propPath, value, true);
+        Assert.assertEquals(1, iv2.size()); // null country.name
+
+        value.getCountry().setName("NWO");
+        iv2 = v.validateValue(Map.class, propPath, value, true);
+        Assert.assertEquals(0, iv2.size());
+    }
+
+    public void testValidateCascadingIndexedElement() throws InvocationTargetException, NoSuchMethodException,
+        IllegalAccessException {
+        final String propPath = "[0]";
+        CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+        Address value = new Address();
+        value.setCity("whatever");
+        value.setAddressline1("1 address line");
+        Set<ConstraintViolation<Address[]>> iv;
+        Address[] array = { value };
+        iv = v.validateProperty(array, propPath, true);
+        Assert.assertEquals(1, iv.size()); // null country
+
+        value.setCountry(new Country());
+        iv = v.validateProperty(array, propPath, true);
+        Assert.assertEquals(1, iv.size()); // null country.name
+
+        value.getCountry().setName("NWO");
+        iv = v.validateProperty(array, propPath, true);
+        Assert.assertEquals(0, iv.size());
+
+        value = new Address();
+        value.setCity("whatever");
+        value.setAddressline1("1 address line");
+        Set<?> iv2;
+        iv2 = v.validateValue(array.getClass(), propPath, value, true);
+        Assert.assertEquals(1, iv2.size()); // null country
+
+        value.setCountry(new Country());
+        iv2 = v.validateValue(array.getClass(), propPath, value, true);
+        Assert.assertEquals(1, iv2.size()); // null country.name
+
+        value.getCountry().setName("NWO");
+        iv2 = v.validateValue(array.getClass(), propPath, value, true);
+        Assert.assertEquals(0, iv2.size());
+    }
+
+    public void testValidateCascadingIndexedGenericElement() throws InvocationTargetException, NoSuchMethodException,
+    IllegalAccessException {
+        final String propPath = "[0]";
+        CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+        Address value = new Address();
+        value.setCity("whatever");
+        value.setAddressline1("1 address line");
+        Set<?> iv;
+        Object list = Collections.singletonList(value);
+        iv = v.validateProperty(list, propPath, true);
+        Assert.assertEquals(1, iv.size()); // null country
+        
+        value.setCountry(new Country());
+        iv = v.validateProperty(list, propPath, true);
+        Assert.assertEquals(1, iv.size()); // null country.name
+        
+        value.getCountry().setName("NWO");
+        iv = v.validateProperty(list, propPath, true);
+        Assert.assertEquals(0, iv.size());
+        
+        value = new Address();
+        value.setCity("whatever");
+        value.setAddressline1("1 address line");
+        Set<?> iv2;
+        iv2 = v.validateValue(List.class, propPath, value, true);
+        Assert.assertEquals(1, iv2.size()); // null country
+        
+        value.setCountry(new Country());
+        iv2 = v.validateValue(List.class, propPath, value, true);
+        Assert.assertEquals(1, iv2.size()); // null country.name
+        
+        value.getCountry().setName("NWO");
+        iv2 = v.validateValue(List.class, propPath, value, true);
+        Assert.assertEquals(0, iv2.size());
+    }
+    
+    public interface Foo {
+    }
+
+    public static class FooAddress extends Address {
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        @NotNull(groups = Foo.class)
+        public String getCity() {
+            return super.getCity();
+        }
+    }
+
+    public void testValidateCascadingPropertyWithMultipleGroupsIgnoresSiblingProperties() {
+        final String propPath = "addresses[0].country";
+
+        CascadingPropertyValidator v = validator.unwrap(CascadingPropertyValidator.class);
+        Author author = new Author();
+        author.setAddresses(new ArrayList<Address>());
+        Address adr = new FooAddress();
+        author.getAddresses().add(adr);
+        Country country = new Country();
+        adr.setCountry(country);
+
+        Set<ConstraintViolation<Author>> iv = v.validateProperty(author, propPath, true, Default.class, Foo.class);
+        Assert.assertEquals(1, iv.size());
+    }
+
+    public void testMetadataAPI() {
+        BeanDescriptor bookBeanDescriptor = validator.getConstraintsForClass(Book.class);
+
+        // expect no constraints on Book's Class-Level
+        Assert.assertFalse(bookBeanDescriptor.hasConstraints());
+        // but there are constraints on Book's Property-Level
+        Assert.assertTrue(bookBeanDescriptor.isBeanConstrained());
+        Assert.assertTrue(bookBeanDescriptor.getConstraintDescriptors().size() == 0); // no
+        // constraint
+        // more specifically "author" and "title"
+        Assert.assertEquals(4, bookBeanDescriptor.getConstrainedProperties().size());
+        // not a property
+        Assert.assertTrue(bookBeanDescriptor.getConstraintsForProperty("doesNotExist") == null);
+        // property with no constraint
+        Assert.assertTrue(bookBeanDescriptor.getConstraintsForProperty("description") == null);
+        PropertyDescriptor propertyDescriptor = bookBeanDescriptor.getConstraintsForProperty("title");
+        Assert.assertEquals(2, propertyDescriptor.getConstraintDescriptors().size());
+        Assert.assertTrue("title".equals(propertyDescriptor.getPropertyName()));
+        // assuming the implementation returns the NotEmpty constraint first
+        Iterator<ConstraintDescriptor<?>> iter = propertyDescriptor.getConstraintDescriptors().iterator();
+        ConstraintDescriptor<?> constraintDescriptor = null;
+        while (iter.hasNext()) {
+            constraintDescriptor = iter.next();
+            if (constraintDescriptor.getAnnotation().annotationType().equals(NotNull.class)) {
+                break;
+            }
+
+        }
+        Assert.assertTrue(constraintDescriptor != null);
+        Assert.assertTrue(constraintDescriptor.getGroups().size() == 1); // "first"
+        Assert.assertEquals(NotNullValidator.class, constraintDescriptor.getConstraintValidatorClasses().get(0));
+        // assuming the implementation returns the Size constraint first
+        propertyDescriptor = bookBeanDescriptor.getConstraintsForProperty("subtitle");
+        Iterator<ConstraintDescriptor<?>> iterator = propertyDescriptor.getConstraintDescriptors().iterator();
+        constraintDescriptor = iterator.next();
+        Assert.assertTrue(constraintDescriptor.getAnnotation().annotationType().equals(Size.class));
+        Assert.assertTrue(((Integer) constraintDescriptor.getAttributes().get("max")) == 30);
+        Assert.assertTrue(constraintDescriptor.getGroups().size() == 1);
+        propertyDescriptor = bookBeanDescriptor.getConstraintsForProperty("author");
+        Assert.assertTrue(propertyDescriptor.getConstraintDescriptors().size() == 1);
+        Assert.assertTrue(propertyDescriptor.isCascaded());
+        Assert.assertNull(bookBeanDescriptor.getConstraintsForProperty("unconstraintField"));
+    }
+
+    public void testKeyedMetadata() {
+        @SuppressWarnings("serial")
+        BeanDescriptor beanDescriptor = validator.getConstraintsForClass(new HashMap<String, Object>() {
+        }.getClass());
+        Assert.assertNotNull(beanDescriptor);
+        Assert.assertFalse(beanDescriptor.isBeanConstrained());
+        Assert.assertNull(beanDescriptor.getConstraintsForProperty("[foo]"));
+    }
+
+    public void testGenericKeyedMetadata() {
+        BeanDescriptor beanDescriptor = validator.getConstraintsForClass(Map.class);
+        Assert.assertNotNull(beanDescriptor);
+        Assert.assertFalse(beanDescriptor.isBeanConstrained());
+        Assert.assertNull(beanDescriptor.getConstraintsForProperty("[foo]"));
+    }
+    
+    public void testIndexedMetadata() {
+        BeanDescriptor beanDescriptor = validator.getConstraintsForClass(Array.newInstance(Author.class, 0).getClass());
+        Assert.assertNotNull(beanDescriptor);
+        Assert.assertFalse(beanDescriptor.isBeanConstrained());
+        Assert.assertNull(beanDescriptor.getConstraintsForProperty("[0]"));
+    }
+    
+    public void testGenericIndexedMetadata() {
+        BeanDescriptor beanDescriptor = validator.getConstraintsForClass(List.class);
+        Assert.assertNotNull(beanDescriptor);
+        Assert.assertFalse(beanDescriptor.isBeanConstrained());
+        Assert.assertNull(beanDescriptor.getConstraintsForProperty("[0]"));
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidatorResolutionTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidatorResolutionTest.java
new file mode 100644
index 0000000..2417582
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/ValidatorResolutionTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.bval.jsr303;
+
+import junit.framework.TestCase;
+
+import javax.validation.*;
+import javax.validation.constraints.NotNull;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.Locale;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+
+/**
+ * Checks the correct behavior of the validator resolution algorithm.
+ * 
+ * @author Carlos Vara
+ */
+public class ValidatorResolutionTest extends TestCase {
+    static ValidatorFactory factory;
+
+    static {
+        factory = Validation.buildDefaultValidatorFactory();
+        ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+    }
+
+    /**
+     * Validator instance to test
+     */
+    protected Validator validator;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        validator = createValidator();
+    }
+
+    /**
+     * Create the validator instance.
+     * 
+     * @return Validator
+     */
+    protected Validator createValidator() {
+        return factory.getValidator();
+    }
+
+    /**
+     * Check that a {@link ConstraintDefinitionException} is thrown when the
+     * only available validator is associated with a different annotation type.
+     */
+    public void testInvalidValidator() {
+        try {
+            validator.validate(new Person());
+            fail("No exception thrown, but no valid validator available.");
+        } catch (ConstraintDefinitionException e) {
+            // correct
+        }
+    }
+
+    public static class Person {
+        @PersonName
+        public String name;
+    }
+
+    @Constraint(validatedBy = { InvalidPersonNameValidator.class })
+    @Documented
+    @Target( { METHOD, FIELD, TYPE })
+    @Retention(RUNTIME)
+    public static @interface PersonName {
+        String message() default "Wrong person name";
+
+        Class<?>[] groups() default {};
+
+        Class<? extends Payload>[] payload() default {};
+    }
+
+    public static class InvalidPersonNameValidator implements ConstraintValidator<NotNull, String> {
+        public void initialize(NotNull constraintAnnotation) {
+            // Nothing
+        }
+
+        public boolean isValid(String value, ConstraintValidatorContext context) {
+            return true;
+        }
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/AccessTestBusinessObject.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/AccessTestBusinessObject.java
new file mode 100644
index 0000000..6066227
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/AccessTestBusinessObject.java
@@ -0,0 +1,73 @@
+/*
+ * 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.bval.jsr303.example;
+
+
+import org.apache.bval.constraints.HasValue;
+
+import javax.validation.Valid;
+
+/**
+ * Description: <br/>
+ */
+public class AccessTestBusinessObject {
+    // test that field-access is used, not method-access 
+    @HasValue({"1", "3"})
+    protected String var1;
+
+    // test that field-access is used, not method-access
+    @SuppressWarnings("unused")
+    @Valid
+    private AccessTestBusinessObject next;
+
+    // not annotated with @Valid, not validated!!
+    private AccessTestBusinessObject toBeIgnored;
+    private AccessTestBusinessObject _next;
+
+    public AccessTestBusinessObject(String var1) {
+        this.var1 = var1;
+    }
+
+    @HasValue("3")
+    public String getVar1() {
+        return "3";
+    }
+
+    public void next(AccessTestBusinessObject next) {
+        this._next = next;
+    }
+
+
+    public void setNext(AccessTestBusinessObject next) {
+        this.next = next;
+    }
+
+    @Valid
+    public AccessTestBusinessObject getNext() {
+        return _next; // method returns '_next', not the field 'next'
+    }
+
+    public AccessTestBusinessObject getToBeIgnored() {
+        return toBeIgnored;
+    }
+
+    public void setToBeIgnored(AccessTestBusinessObject toBeIgnored) {
+        this.toBeIgnored = toBeIgnored;
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/AccessTestBusinessObjectSub.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/AccessTestBusinessObjectSub.java
new file mode 100644
index 0000000..3aa3b5e
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/AccessTestBusinessObjectSub.java
@@ -0,0 +1,57 @@
+/*
+ * 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.bval.jsr303.example;
+
+import org.apache.bval.constraints.HasValue;
+
+/**
+ * Description: <br/>
+ */
+public class AccessTestBusinessObjectSub extends AccessTestBusinessObject {
+    private String var2, _var2;
+
+    public void setVar2(String var2) {
+        this.var2 = var2;
+    }
+
+    public void setvar2(String _var2) {
+        this._var2 = _var2;
+    }
+
+    public AccessTestBusinessObjectSub(String var1) {
+        super(var1);
+    }
+
+    // getVar1() is called on subclass, although annotated on superclass    
+    public String getVar1() {
+        return var1;
+    }
+
+    //// test that getvar2() is called, not getVar2()
+
+    @HasValue("5")
+    public String getvar2() {
+        return _var2;
+    }
+
+    @HasValue("6")
+    public String getVar2() {
+        return var2;
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Address.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Address.java
new file mode 100644
index 0000000..d72f2fc
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Address.java
@@ -0,0 +1,100 @@
+/*
+ * 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.bval.jsr303.example;
+
+
+import org.apache.bval.constraints.ZipCodeCityCoherence;
+
+import javax.validation.GroupSequence;
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import javax.validation.groups.Default;
+
+@ZipCodeCityCoherence
+public class Address implements ZipCodeCityCarrier {
+    @NotNull
+    @Size(max = 30)
+    private String addressline1;
+    @Size(max = 30)
+    private String addressline2;
+    @Size(max = 11)
+    private String zipCode;
+    @NotNull
+    @Valid
+    private Country country;
+    private String city;
+
+    public String getAddressline1() {
+        return addressline1;
+    }
+
+    public void setAddressline1(String addressline1) {
+        this.addressline1 = addressline1;
+    }
+
+    public String getAddressline2() {
+        return addressline2;
+    }
+
+    public void setAddressline2(String addressline2) {
+        this.addressline2 = addressline2;
+    }
+
+    public String getZipCode() {
+        return zipCode;
+    }
+
+    public void setZipCode(String zipCode) {
+        this.zipCode = zipCode;
+    }
+
+    @Size(max = 30)
+    @NotNull
+    public String getCity() {
+        return city;
+    }
+
+    public void setCity(String city) {
+        this.city = city;
+    }
+
+    public Country getCountry() {
+        return country;
+    }
+
+    public void setCountry(Country country) {
+        this.country = country;
+    }
+
+    /**
+     * Check conherence on the overall object
+     * Needs basic checking to be green first
+     */
+    public interface HighLevelCoherence {
+    }
+
+    /**
+     * Check both basic constraints and high level ones.
+     * High level constraints are not checked if basic constraints fail.
+     */
+    @GroupSequence(value = {Default.class, HighLevelCoherence.class})
+    public interface Complete {
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Author.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Author.java
new file mode 100644
index 0000000..68e2ca8
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Author.java
@@ -0,0 +1,72 @@
+/*
+ * 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.bval.jsr303.example;
+
+
+import org.apache.bval.constraints.NotEmpty;
+
+import javax.validation.GroupSequence;
+import javax.validation.Valid;
+import javax.validation.constraints.Size;
+import java.util.List;
+
+@GroupSequence({First.class, Author.class, Last.class})
+public class Author {
+    @NotEmpty(groups = Last.class)
+    private String firstName;
+    @NotEmpty(groups = First.class)
+    private String lastName;
+    @Size(max = 40, groups = First.class)
+    private String company;
+
+    @Valid
+    private List<Address> addresses;
+
+    public List<Address> getAddresses() {
+        return addresses;
+    }
+
+    public void setAddresses(List<Address> addresses) {
+        this.addresses = addresses;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+
+    public String getCompany() {
+        return company;
+    }
+
+    public void setCompany(String company) {
+        this.company = company;
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Book.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Book.java
new file mode 100644
index 0000000..0d5c7ac
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Book.java
@@ -0,0 +1,83 @@
+/*
+ * 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.bval.jsr303.example;
+
+
+import org.apache.bval.constraints.NotEmpty;
+
+import javax.validation.GroupSequence;
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+@GroupSequence({First.class, Second.class, Book.class, Last.class})
+public class Book {
+    @NotNull(groups = First.class)
+    @NotEmpty(groups = First.class)
+    private String title;
+
+    @Size(max = 30, groups = Second.class)
+    private String subtitle;
+
+    @Valid
+    @NotNull(groups = First.class)
+    private Author author;
+
+    @SuppressWarnings("unused")
+    @NotNull
+    private int uselessField;
+
+    private int unconstraintField;
+
+    public int getUnconstraintField() {
+        return unconstraintField;
+    }
+
+    public void setUnconstraintField(int unconstraintField) {
+        this.unconstraintField = unconstraintField;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getSubtitle() {
+        return subtitle;
+    }
+
+    public void setSubtitle(String subtitle) {
+        this.subtitle = subtitle;
+    }
+
+    public Author getAuthor() {
+        return author;
+    }
+
+    public void setAuthor(Author author) {
+        this.author = author;
+    }
+
+    @GroupSequence(value = {First.class, Second.class, Last.class})
+    public interface All {
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/BusinessAddress.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/BusinessAddress.java
new file mode 100644
index 0000000..2921041
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/BusinessAddress.java
@@ -0,0 +1,37 @@
+/*
+ * 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.bval.jsr303.example;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * Description: <br/>
+ */
+public class BusinessAddress extends Address {
+    private String company;
+
+    @NotNull
+    public String getCompany() {
+        return company;
+    }
+
+    public void setCompany(String company) {
+        this.company = company;
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/CompanyAddress.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/CompanyAddress.java
new file mode 100644
index 0000000..02973a6
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/CompanyAddress.java
@@ -0,0 +1,40 @@
+/*
+ * 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.bval.jsr303.example;
+
+import org.apache.bval.constraints.CompanyEmail;
+
+/**
+ * Description: <br/>
+ */
+public class CompanyAddress {
+    @SuppressWarnings("unused")
+    @CompanyEmail
+    private String email;
+
+    public CompanyAddress() {
+    }
+
+    public CompanyAddress(String email) {
+        this.email = email;
+    }
+
+    // do not provided getters & setters to test that value access
+    // of combined constraints directly use the private field 'email'
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Continent.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Continent.java
new file mode 100644
index 0000000..78be5c3
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Continent.java
@@ -0,0 +1,40 @@
+/*
+ *  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.bval.jsr303.example;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A continent has a name and a set of {@link Country}s.
+ * 
+ * @author Carlos Vara
+ */
+public class Continent {
+
+    @NotNull
+    public String name;
+    
+    @Valid
+    public Set<Country> countries = new HashSet<Country>();
+    
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Country.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Country.java
new file mode 100644
index 0000000..ffb408d
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Country.java
@@ -0,0 +1,56 @@
+/*
+ * 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.bval.jsr303.example;
+
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+public class Country {
+    @NotNull
+    private String name;
+    @Size(max = 2)
+    private String ISO2Code;
+    @Size(max = 3)
+    private String ISO3Code;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getISO2Code() {
+        return ISO2Code;
+    }
+
+    public void setISO2Code(String ISO2Code) {
+        this.ISO2Code = ISO2Code;
+    }
+
+    public String getISO3Code() {
+        return ISO3Code;
+    }
+
+    public void setISO3Code(String ISO3Code) {
+        this.ISO3Code = ISO3Code;
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Customer.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Customer.java
new file mode 100644
index 0000000..9e74f1c
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Customer.java
@@ -0,0 +1,86 @@
+/*
+ * 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.bval.jsr303.example;
+
+
+import org.apache.bval.constraints.Email;
+import org.apache.bval.constraints.Password;
+
+import javax.validation.constraints.NotNull;
+
+public class Customer implements Person {
+    private String firstName;
+    private String middleName;
+    private String lastName;
+    @NotNull
+    private String customerId;
+    @Password(robustness = 5)
+    private String password;
+
+    @Email
+    private String emailAddress;
+
+    public String getEmailAddress() {
+        return emailAddress;
+    }
+
+    public void setEmailAddress(String emailAddress) {
+        this.emailAddress = emailAddress;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getMiddleName() {
+        return middleName;
+    }
+
+    public void setMiddleName(String middleName) {
+        this.middleName = middleName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+
+    public String getCustomerId() {
+        return customerId;
+    }
+
+    public void setCustomerId(String customerId) {
+        this.customerId = customerId;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Employee.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Employee.java
new file mode 100644
index 0000000..4efe01d
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Employee.java
@@ -0,0 +1,51 @@
+/*
+ * 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.bval.jsr303.example;
+
+/**
+ * Description: <br/>
+ */
+public class Employee implements Person {
+    private String firstName, lastName;
+
+    public Employee(String firstN, String lastN) {
+        this.firstName = firstN;
+        this.lastName = lastN;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public String getMiddleName() {
+        return null;  // not supported
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Engine.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Engine.java
new file mode 100644
index 0000000..071cb3d
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Engine.java
@@ -0,0 +1,32 @@
+/*
+ * 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.bval.jsr303.example;
+
+import javax.validation.constraints.Pattern;
+
+public class Engine {
+    @Pattern.List({
+        @Pattern(regexp = "^[A-Z0-9-]+$", flags = Pattern.Flag.CASE_INSENSITIVE,
+                message = "must contain alphabetical characters only"),
+        @Pattern(
+                regexp = "^....-....-....$", message = "must match ....-....-....")})
+    public String serialNumber;
+
+
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/First.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/First.java
new file mode 100644
index 0000000..d8f688f
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/First.java
@@ -0,0 +1,25 @@
+/*
+ * 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.bval.jsr303.example;
+
+/**
+ * Description: <br/>
+ */
+public interface First {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/FrenchAddress.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/FrenchAddress.java
new file mode 100644
index 0000000..cfa468b
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/FrenchAddress.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.bval.jsr303.example;
+
+import org.apache.bval.constraints.FrenchZipCode;
+
+/**
+ * Description: <br/>
+ */
+public class FrenchAddress {
+    @FrenchZipCode(size = 7)
+    String zipCode;
+
+    public FrenchAddress() {
+    }
+
+    public FrenchAddress(String zipCode) {
+        this.zipCode = zipCode;
+    }
+    
+    public String getZipCode() {
+        return zipCode;
+    }
+
+    public void setZipCode(String zipCode) {
+        this.zipCode = zipCode;
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/IllustratedBook.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/IllustratedBook.java
new file mode 100644
index 0000000..89993ad
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/IllustratedBook.java
@@ -0,0 +1,41 @@
+/*
+ *  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.bval.jsr303.example;
+
+/**
+ * Add a non-cascaded bean to a book.
+ * 
+ * @version $Rev: 1004764 $ $Date: 2010-10-05 13:35:42 -0500 (Tue, 05 Oct 2010) $
+ */
+public class IllustratedBook extends Book {
+    private Person illustrator;
+
+    /**
+     * @return the illustrator
+     */
+    public Person getIllustrator() {
+        return illustrator;
+    }
+
+    /**
+     * @param illustrator
+     *            the illustrator to set
+     */
+    public void setIllustrator(Person illustrator) {
+        this.illustrator = illustrator;
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Last.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Last.java
new file mode 100644
index 0000000..558a60a
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Last.java
@@ -0,0 +1,25 @@
+/*
+ * 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.bval.jsr303.example;
+
+/**
+ * Description: <br/>
+ */
+public interface Last {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Library.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Library.java
new file mode 100644
index 0000000..b6d3306
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Library.java
@@ -0,0 +1,73 @@
+/*
+ * 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.bval.jsr303.example;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+
+/**
+ * Description: <br/>
+ */
+public class Library {
+    @NotNull
+    private String libraryName;
+    @Valid
+    private final Map<String, Book> taggedBooks = new HashMap<String, Book>();
+
+    private Person[] persons;
+
+    public String getLibraryName() {
+        return libraryName;
+    }
+
+    public void setLibraryName(String libraryName) {
+        this.libraryName = libraryName;
+    }
+
+    public Map<String, Book> getTaggedBooks() {
+        return taggedBooks;
+    }
+
+    public Person[] getPersons() {
+        return persons;
+    }
+
+    public void setPersons(Person[] persons) {
+        this.persons = persons;
+    }
+
+    @Valid
+    public List<Employee> getEmployees() {
+        if (persons == null)
+            return Collections.emptyList();
+
+        ArrayList<Employee> emps = new ArrayList<Employee>(persons.length);
+        for (Person each : persons) {
+            if (each instanceof Employee)
+                emps.add((Employee) each);
+        }
+        return emps;
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/MaxTestEntity.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/MaxTestEntity.java
new file mode 100644
index 0000000..3af3f99
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/MaxTestEntity.java
@@ -0,0 +1,70 @@
+/*
+ * 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.bval.jsr303.example;
+
+import javax.validation.constraints.Max;
+import java.math.BigDecimal;
+
+/**
+ * Description: <br/>
+ */
+public class MaxTestEntity {
+    @Max(100)
+    private String text;
+    private String property;
+
+    @Max(300)
+    private long longValue;
+
+    private BigDecimal decimalValue;
+
+    public String getText() {
+        return text;
+    }
+
+    @Max(200)
+    public String getProperty() {
+        return property;
+    }
+
+    public long getLongValue() {
+        return longValue;
+    }
+
+    @Max(400)
+    public BigDecimal getDecimalValue() {
+        return decimalValue;
+    }
+
+    public void setText(String text) {
+        this.text = text;
+    }
+
+    public void setProperty(String property) {
+        this.property = property;
+    }
+
+    public void setLongValue(long longValue) {
+        this.longValue = longValue;
+    }
+
+    public void setDecimalValue(BigDecimal decimalValue) {
+        this.decimalValue = decimalValue;
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/NoValidatorTestEntity.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/NoValidatorTestEntity.java
new file mode 100644
index 0000000..206ac7c
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/NoValidatorTestEntity.java
@@ -0,0 +1,31 @@
+/*
+ * 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.bval.jsr303.example;
+
+import javax.validation.constraints.Max;
+
+/**
+ * Description: <br/>
+ */
+public class NoValidatorTestEntity {
+    @SuppressWarnings("unused")
+    @Max(20)
+    private Object anything;
+    
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Person.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Person.java
new file mode 100644
index 0000000..b6012d7
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Person.java
@@ -0,0 +1,31 @@
+/*
+ * 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.bval.jsr303.example;
+
+import org.apache.bval.constraints.NotEmpty;
+
+public interface Person {
+    @NotEmpty
+    String getFirstName();
+
+    String getMiddleName();
+
+    @NotEmpty
+    String getLastName();
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/PreferredGuest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/PreferredGuest.java
new file mode 100644
index 0000000..ff9f10c
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/PreferredGuest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.bval.jsr303.example;
+
+import javax.validation.constraints.Digits;
+
+public class PreferredGuest extends Customer {
+    @Digits(integer = 10, fraction = 0)
+    private String guestCreditCardNumber;
+
+    public String getGuestCreditCardNumber() {
+        return guestCreditCardNumber;
+    }
+
+    public void setGuestCreditCardNumber(String guestCreditCardNumber) {
+        this.guestCreditCardNumber = guestCreditCardNumber;
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/RecursiveFoo.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/RecursiveFoo.java
new file mode 100644
index 0000000..928a2f9
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/RecursiveFoo.java
@@ -0,0 +1,43 @@
+/*
+ * 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.bval.jsr303.example;
+
+
+import org.apache.bval.constraints.NotEmpty;
+
+import javax.validation.Valid;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Description: <br/>
+ */
+public class RecursiveFoo {
+    @NotEmpty
+    @Valid
+    Collection<RecursiveFoo> foos = new ArrayList<RecursiveFoo>();
+
+    public Collection<RecursiveFoo> getFoos() {
+        return foos;
+    }
+
+    public void setFoos(Collection<RecursiveFoo> foos) {
+        this.foos = foos;
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Second.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Second.java
new file mode 100644
index 0000000..e67cbee
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/Second.java
@@ -0,0 +1,25 @@
+/*
+ * 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.bval.jsr303.example;
+
+/**
+ * Description: <br/>
+ */
+public interface Second {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/SizeTestEntity.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/SizeTestEntity.java
new file mode 100644
index 0000000..2211f2d
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/SizeTestEntity.java
@@ -0,0 +1,56 @@
+/*
+ * 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.bval.jsr303.example;
+
+import javax.validation.constraints.Size;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Description: <br/>
+ */
+public class SizeTestEntity {
+    @Size(max=2)
+    public Map<String,String> map;
+    @Size(max=2)
+    public Collection<String> coll;
+    @Size(max=2)
+    public String text;
+
+    @Size(max=2)
+    public Object[] oa;
+    @Size(max=2)
+    public byte[] ba;
+    @Size(max=2)
+    public int[] it;
+    @Size(max=2)
+    public Integer[] oa2;
+    @Size(max=2)
+    public boolean[] boa;
+    @Size(max=2)
+    public char[] ca;
+    @Size(max=2)
+    public double[] da;
+    @Size(max=2)
+    public float[] fa;
+    @Size(max=2)
+    public long[] la;
+    @Size(max=2)
+    public short[] sa;
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/XmlEntitySampleBean.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/XmlEntitySampleBean.java
new file mode 100644
index 0000000..8ffefb0
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/XmlEntitySampleBean.java
@@ -0,0 +1,53 @@
+/*
+ * 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.bval.jsr303.example;
+
+/**
+ * Description: bean used to test constraints described in XML<br/>
+ */
+public class XmlEntitySampleBean {
+    private String zipCode;
+    private String valueCode;
+
+    private String firstName;
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getZipCode() {
+        return zipCode;
+    }
+
+    public void setZipCode(String zipCode) {
+        this.zipCode = zipCode;
+    }
+
+    public String getValueCode() {
+        return valueCode;
+    }
+
+    public void setValueCode(String valueCode) {
+        this.valueCode = valueCode;
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/ZipCodeCityCarrier.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/ZipCodeCityCarrier.java
new file mode 100644
index 0000000..41fada4
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/example/ZipCodeCityCarrier.java
@@ -0,0 +1,27 @@
+/*
+ * 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.bval.jsr303.example;
+
+/**
+ * Description: <br/>
+ */
+public interface ZipCodeCityCarrier {
+    String getZipCode();
+    String getCity();
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/extensions/ExampleMethodService.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/extensions/ExampleMethodService.java
new file mode 100644
index 0000000..d3a2432
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/extensions/ExampleMethodService.java
@@ -0,0 +1,69 @@
+/*
+ * 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.bval.jsr303.extensions;
+
+
+import org.apache.bval.constraints.NotEmpty;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+
+/**
+ * Description: class with annotated methods to demonstrate
+ * method-level-validation<br/>
+ */
+public class ExampleMethodService {
+    public ExampleMethodService() {
+    }
+
+    public ExampleMethodService(@NotNull @NotEmpty String s1, @NotNull String s2) {
+    }
+
+    @NotNull
+    @NotEmpty
+    public String concat(@NotNull @NotEmpty String s1, @NotNull String s2) {
+        return s1 + s2;
+    }
+    
+    public void save(@Pattern(regexp="[a-f0-9]{4}") String data) {
+    	return;
+    }
+    
+    @NotNull
+    @Size(min=3,max=10)
+    public String echo(@NotNull @Size(min=3,max=10) String str) {
+    	return str;
+    }
+    
+    public void personOp1(@Valid Person p) {
+        return;
+    }
+    
+    public void personOp2(@NotNull @Valid Person p) {
+        return;
+    }
+    
+    public static class Person {
+        @NotNull
+        String name;
+    }
+    
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/extensions/MethodValidatorImplTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/extensions/MethodValidatorImplTest.java
new file mode 100644
index 0000000..21b33a7
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/extensions/MethodValidatorImplTest.java
@@ -0,0 +1,289 @@
+/*
+ *  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.bval.jsr303.extensions;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.apache.bval.jsr303.ApacheValidationProvider;
+import org.apache.bval.jsr303.ApacheValidatorConfiguration;
+import org.apache.bval.jsr303.ClassValidator;
+import org.apache.bval.jsr303.extensions.ExampleMethodService.Person;
+
+import javax.validation.Validation;
+import javax.validation.ValidationException;
+import javax.validation.Validator;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Set;
+
+/**
+ * MethodValidatorImpl Tester.
+ *
+ * @author <Authors name>
+ * @version 1.0
+ * @since <pre>11/11/2009</pre>
+ */
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public class MethodValidatorImplTest extends TestCase {
+    public MethodValidatorImplTest(String name) {
+        super(name);
+    }
+
+    public static Test suite() {
+        return new TestSuite(MethodValidatorImplTest.class);
+    }
+
+    public void testUnwrap() {
+        Validator v = getValidator();
+        ClassValidator cv = v.unwrap(ClassValidator.class);
+        assertTrue(v == cv);
+        assertTrue(v == v.unwrap(Validator.class));
+        MethodValidatorImpl mvi = v.unwrap(MethodValidatorImpl.class);
+        assertNotNull(mvi);
+        MethodValidator mv = v.unwrap(MethodValidator.class);
+        assertNotNull(mv);
+        assertTrue(mv == mv.unwrap(MethodValidatorImpl.class));
+        assertTrue(mv == mv.unwrap(ClassValidator.class));
+    }
+
+    public void testValidateMethodParameters() throws NoSuchMethodException {
+        ExampleMethodService service = new ExampleMethodService();
+        MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+        Method method =
+              service.getClass().getMethod("concat", new Class[]{String.class, String.class});
+        String[] params = new String[2];
+        params[0] = "Hello ";
+        params[1] = "world";
+        Set results = mv.validateParameters(service.getClass(), method, params);
+        assertEquals(true, results.isEmpty());
+
+        params[0] = "";
+        results = mv.validateParameters(service.getClass(), method, params);
+        assertEquals(1, results.size());
+
+        params[1] = null;
+        results = mv.validateParameters(service.getClass(), method, params);
+        assertEquals(2, results.size());
+
+        results = mv.validateParameter(service.getClass(), method,  params[0], 0);
+        assertEquals(1, results.size());
+
+        results = mv.validateParameter(service.getClass(), method,  "ok", 0);
+        assertEquals(0, results.size());
+    }
+    
+    public void testValidateMoreMethodParameters() throws NoSuchMethodException {
+    	
+    	ExampleMethodService service = new ExampleMethodService();
+    	MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+    	Method saveMethod = service.getClass().getMethod("save", new Class[]{String.class});
+    	
+    	String[] saveParams = new String[1];
+    	saveParams[0] = "abcd";
+    	
+    	Set results = mv.validateParameters(service.getClass(), saveMethod, saveParams);
+    	assertTrue(results.isEmpty());
+    	
+    	saveParams[0] = "zzzz";
+    	results = mv.validateParameters(service.getClass(), saveMethod, saveParams);
+    	assertEquals(1, results.size());
+    	
+    	Method echoMethod = service.getClass().getMethod("echo", new Class[]{String.class});
+    	
+    	String[] echoParams = new String[1];
+    	echoParams[0] = "hello";
+    	
+    	results = mv.validateParameters(service.getClass(), echoMethod, echoParams);
+    	assertTrue(results.isEmpty());
+    	
+    	echoParams[0] = "h";
+    	results = mv.validateParameters(service.getClass(), echoMethod, echoParams);
+    	assertEquals(1, results.size());
+    	
+    	echoParams[0] = null;
+    	results = mv.validateParameters(service.getClass(), echoMethod, echoParams);
+    	assertEquals(1, results.size());
+    	
+    }
+
+    public void testValidateConstructorParameters() throws NoSuchMethodException {
+        ExampleMethodService service = new ExampleMethodService();
+        MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+        Constructor constructor =
+              service.getClass().getConstructor(String.class, String.class);
+        String[] params = new String[2];
+        params[0] = "Hello ";
+        params[1] = "world";
+        Set results = mv.validateParameters(service.getClass(), constructor, params);
+        assertEquals(true, results.isEmpty());
+
+        params[0] = "";
+        results = mv.validateParameters(service.getClass(), constructor, params);
+        assertEquals(1, results.size());
+
+        params[1] = null;
+        results = mv.validateParameters(service.getClass(), constructor, params);
+        assertEquals(2, results.size());
+
+        results = mv.validateParameter(service.getClass(), constructor,  params[0], 0);
+        assertEquals(1, results.size());
+
+        results = mv.validateParameter(service.getClass(), constructor,  "ok", 0);
+        assertEquals(0, results.size());
+    }
+
+    public void testValidateReturnValue() throws NoSuchMethodException {
+        ExampleMethodService service = new ExampleMethodService();
+        MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+        Method method =
+              service.getClass().getMethod("concat", new Class[]{String.class, String.class});
+        Set results;
+        results = mv.validateReturnedValue(service.getClass(), method, "test");
+        assertEquals(true, results.isEmpty());
+
+        results = mv.validateReturnedValue(service.getClass(), method, "");
+        assertEquals(1, results.size());
+    }
+    
+    public void testValidateMoreReturnValue() throws NoSuchMethodException {
+    	ExampleMethodService service = new ExampleMethodService();
+    	MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+    	Method echoMethod = service.getClass().getMethod("echo", new Class[]{String.class});
+    	
+    	String returnedValue = "a too long string";
+    	Set results = mv.validateReturnedValue(service.getClass(), echoMethod, returnedValue);
+    	assertEquals(1, results.size());
+    	
+    	returnedValue = null;
+    	results = mv.validateReturnedValue(service.getClass(), echoMethod, returnedValue);
+    	assertEquals(1, results.size());
+    	
+    	returnedValue = "valid";
+    	results = mv.validateReturnedValue(service.getClass(), echoMethod, returnedValue);
+    	assertTrue(results.isEmpty());
+    }
+    
+    public void testValidateValidParam() throws NoSuchMethodException {
+        ExampleMethodService service = new ExampleMethodService();
+        MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+        
+        Method personOp1 = service.getClass().getMethod("personOp1", new Class[]{Person.class});
+        
+        // Validate with invalid person
+        Person p = new ExampleMethodService.Person();
+        Set<?> results = mv.validateParameters(service.getClass(), personOp1, new Object[]{p});
+        assertEquals("Expected 1 violation", 1, results.size());
+        
+        // validate with valid person
+        p.name = "valid name";
+        results = mv.validateParameters(service.getClass(), personOp1, new Object[]{p});
+        assertTrue("No violations expected", results.isEmpty());
+        
+        // validate with null person
+        results = mv.validateParameters(service.getClass(), personOp1, new Object[]{null});
+        assertTrue("No violations expected", results.isEmpty());
+    }
+    
+    public void testValidateNotNullValidParam() throws NoSuchMethodException {
+        ExampleMethodService service = new ExampleMethodService();
+        MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+        
+        Method personOp2 = service.getClass().getMethod("personOp2", new Class[]{Person.class});
+        
+        // Validate with null person
+        Set<?> results = mv.validateParameters(service.getClass(), personOp2, new Object[]{null});
+        assertEquals("Expected 1 violation", 1, results.size());
+        
+        // Validate with invalid person
+        Person p = new ExampleMethodService.Person();
+        results = mv.validateParameters(service.getClass(), personOp2, new Object[]{p});
+        assertEquals("Expected 1 violation", 1, results.size());
+        
+        // validate with valid person
+        p.name = "valid name";
+        results = mv.validateParameters(service.getClass(), personOp2, new Object[]{p});
+        assertTrue("No violations expected", results.isEmpty());
+    }
+    
+    
+    /**
+     * Validate a method defined in an interface using the following combinations:
+     * <ul>
+     *  <li>impl.class + impl.method</li>
+     *  <li>interface.class + interface.method</li>
+     *  <li>impl.class + interface.method</li>
+     *  <li>interface.class + impl.method</li>
+     * </ul>
+     */
+    public void testValidateImplementedMethod() throws NoSuchMethodException {
+        UserMethodsImpl um = new UserMethodsImpl();
+        MethodValidator mv = getValidator().unwrap(MethodValidator.class);
+        
+        Method classMethod = um.getClass().getMethod("findUser", new Class[]{String.class, String.class, Integer.class});
+        Method ifaceMethod = UserMethods.class.getMethod("findUser", new Class[]{String.class, String.class, Integer.class});
+        
+        Set<?> results;
+        
+        // Validate from class (should create violations)
+        results = mv.validateParameters(um.getClass(), classMethod, new Object[]{"", "valid", null });
+        assertEquals("Invalid number of violations", 2, results.size());
+        
+        // Validate from interface
+        results = mv.validateParameters(UserMethods.class, ifaceMethod, new Object[]{"", "valid", null });
+        assertEquals("Invalid number of violations", 0, results.size());
+        
+        // Invalid combinations
+        try {
+            results = mv.validateParameters(UserMethods.class, classMethod, new Object[]{"", "valid", null });
+            Assert.fail("Exception not thrown when validating interface.class + impl.method");
+        } catch (ValidationException e) {
+            // Expected
+        }
+        try {
+            results = mv.validateParameters(um.getClass(), ifaceMethod, new Object[]{"", "valid", null });
+            Assert.fail("Exception not thrown when validating impl.class + interface.method");
+        } catch (ValidationException e) {
+            // Expected
+        }
+        
+    }
+    
+    public static interface UserMethods {
+        void findUser(String param1, String param2, Integer param3);
+    }
+    
+    public static class UserMethodsImpl implements UserMethods {
+        // @Override - not allowed in 1.5 for Interface methods
+        public void findUser( @Size( min=1 ) String param1, @NotNull String param2, @NotNull Integer param3) {
+            return;
+        }        
+    }
+    
+
+    private Validator getValidator() {
+        return Validation
+            .byProvider(ApacheValidationProvider.class)
+            .configure()
+            .addProperty(ApacheValidatorConfiguration.Properties.METABEAN_FACTORY_CLASSNAMES,
+                MethodValidatorMetaBeanFactory.class.getName()).buildValidatorFactory().getValidator();
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/Billable.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/Billable.java
new file mode 100644
index 0000000..4db8fa4
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/Billable.java
@@ -0,0 +1,26 @@
+/*
+ * 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.bval.jsr303.groups;
+
+/**
+ * Validation group checking a user is billable.
+ * Example 3.1. Definition of groups
+ */
+public interface Billable {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BillableCreditCard.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BillableCreditCard.java
new file mode 100644
index 0000000..11bebac
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BillableCreditCard.java
@@ -0,0 +1,25 @@
+/*
+ * 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.bval.jsr303.groups;
+
+/**
+ * Example 3.2. Assign groups to constraints.
+ */
+public class BillableCreditCard {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BillableUser.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BillableUser.java
new file mode 100644
index 0000000..ddf3e57
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BillableUser.java
@@ -0,0 +1,61 @@
+/*
+ * 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.bval.jsr303.groups;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.groups.Default;
+
+/**
+ * User representation
+ * Example 3.2. Assign groups to constraints.
+ */
+public class BillableUser {
+    @NotNull
+    private String firstname;
+
+    @NotNull(groups = Default.class)
+    private String lastname;
+
+    @NotNull(groups = {Billable.class, BuyInOneClick.class})
+    private BillableCreditCard defaultCreditCard;
+
+    public String getFirstname() {
+        return firstname;
+    }
+
+    public void setFirstname(String firstname) {
+        this.firstname = firstname;
+    }
+
+    public String getLastname() {
+        return lastname;
+    }
+
+    public void setLastname(String lastname) {
+        this.lastname = lastname;
+    }
+
+    public BillableCreditCard getDefaultCreditCard() {
+        return defaultCreditCard;
+    }
+
+    public void setDefaultCreditCard(BillableCreditCard defaultCreditCard) {
+        this.defaultCreditCard = defaultCreditCard;
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BuyInOneClick.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BuyInOneClick.java
new file mode 100644
index 0000000..bd536bf
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/BuyInOneClick.java
@@ -0,0 +1,26 @@
+/*
+ * 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.bval.jsr303.groups;
+
+/**
+ * customer can buy without any harrassing checking process.
+ * Example 3.1. Definition of groups
+ */
+public interface BuyInOneClick {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CollectionValidationTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CollectionValidationTest.java
new file mode 100644
index 0000000..d6e6ddf
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CollectionValidationTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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.bval.jsr303.groups;
+
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+import junit.framework.TestCase;
+
+import org.apache.bval.jsr303.DefaultMessageInterpolator;
+import org.apache.bval.jsr303.example.Address;
+import org.apache.bval.jsr303.example.Author;
+import org.apache.bval.jsr303.example.Book;
+import org.apache.bval.jsr303.example.Country;
+import org.apache.bval.jsr303.example.Customer;
+import org.apache.bval.jsr303.example.Employee;
+import org.apache.bval.jsr303.example.Library;
+import org.apache.bval.jsr303.example.Person;
+import org.apache.bval.jsr303.util.TestUtils;
+
+/**
+ * Description: <br/>
+ */
+public class CollectionValidationTest extends TestCase {
+    static ValidatorFactory factory;
+
+    static {
+        factory = Validation.buildDefaultValidatorFactory();
+        ((DefaultMessageInterpolator) factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+    }
+
+    /**
+     * Validator instance to test
+     */
+    protected Validator validator;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        validator = createValidator();
+    }
+
+    /**
+     * Create the validator instance.
+     * 
+     * @return Validator
+     */
+    protected Validator createValidator() {
+        return factory.getValidator();
+    }
+
+    public void testValidateList() {
+        Author author = new Author();
+        author.setFirstName("Peter");
+        author.setLastName("Ford");
+        author.setCompany("IBM");
+        author.setAddresses(new ArrayList<Address>());
+
+        Address adr1, adr2, adr3;
+        adr1 = new Address();
+        adr1.setCountry(new Country());
+        adr1.getCountry().setName("Germany");
+        adr1.setCity("Bonn");
+        adr1.setAddressline1("Strasse 1");
+
+        adr2 = new Address();
+        adr2.setCountry(new Country());
+        adr2.getCountry().setName("Cuba");
+        adr2.setCity("Habana");
+        adr2.setAddressline1("Calle 2");
+
+        adr3 = new Address();
+        adr3.setCountry(new Country());
+        adr3.getCountry().setName("USA");
+        adr3.setCity("San Francisco");
+        adr3.setAddressline1("Street 3");
+
+        author.getAddresses().add(adr1);
+        author.getAddresses().add(adr2);
+        author.getAddresses().add(adr3);
+
+        Set<ConstraintViolation<Author>> violations;
+
+        violations = validator.validate(author);
+        assertEquals(0, violations.size());
+
+        adr2.setCity(null); // violate not null
+        adr3.setAddressline1(null); // violate not null
+
+        violations = validator.validate(author);
+        assertEquals(2, violations.size());
+        assertNotNull(TestUtils.getViolation(violations, "addresses[1].city"));
+        assertNotNull(TestUtils.getViolation(violations, "addresses[2].addressline1"));
+    }
+
+    public void testValidateMapAndRedefinedDefaultGroupOnNonRootBean() {
+        Library lib = new Library();
+        lib.setLibraryName("Leibnitz Bibliothek");
+
+        Book book1, book2, book3;
+
+        book1 = new Book();
+        book1.setTitle("History of time");
+        book1.setSubtitle("How it really works");
+        Author hawking = new Author();
+        hawking.setFirstName("Stephen");
+        hawking.setFirstName("Hawking");
+        hawking.setAddresses(new ArrayList<Address>(1));
+        Address adr = new Address();
+        adr.setAddressline1("Street 1");
+        adr.setCity("London");
+        adr.setCountry(new Country());
+        adr.getCountry().setName("England");
+        hawking.getAddresses().add(adr);
+        book1.setAuthor(hawking);
+
+        book2 = new Book();
+        Author castro = new Author();
+        castro.setFirstName("Fidel");
+        castro.setLastName("Castro Ruz");
+        book2.setAuthor(castro);
+        book2.setTitle("My life");
+
+        book3 = new Book();
+        book3.setTitle("World best jokes");
+        Author someone = new Author();
+        someone.setFirstName("John");
+        someone.setLastName("Do");
+        book3.setAuthor(someone);
+
+        lib.getTaggedBooks().put("science", book1);
+        lib.getTaggedBooks().put("politics", book2);
+        lib.getTaggedBooks().put("humor", book3);
+
+        Set<ConstraintViolation<Library>> violations;
+
+        violations = validator.validate(lib);
+        assertTrue(violations.isEmpty());
+
+        book2.setTitle(null);
+        book3.getAuthor().setFirstName(""); // violate NotEmpty validation
+        book1.getAuthor().getAddresses().get(0).setCity(null);
+        /*
+         * This, by the way, tests redefined default group sequence behavior on
+         * non-root-beans (Library.Book)!!
+         */
+        violations = validator.validate(lib);
+        assertEquals("redefined default group of Book not correctly validated from Library", 3, violations.size());
+        assertNotNull(TestUtils.getViolation(violations, "taggedBooks[politics].title"));
+        assertNotNull(TestUtils.getViolation(violations, "taggedBooks[humor].author.firstName"));
+        assertNotNull(TestUtils.getViolation(violations, "taggedBooks[science].author.addresses[0].city"));
+    }
+
+    public void testValidateArray() {
+        Library lib = new Library();
+        lib.setLibraryName("Unibibliothek");
+        lib.setPersons(new Person[3]);
+        lib.getPersons()[0] = new Employee("Marcel", "Reich-Ranicki");
+        lib.getPersons()[1] = new Employee("Elke", "Heidenreich");
+        lib.getPersons()[2] = new Customer(); // not validated, because only
+        // getEmployees() is @Valid
+
+        Set<ConstraintViolation<Library>> violations;
+        violations = validator.validate(lib);
+        assertTrue(violations.isEmpty());
+
+        ((Employee) lib.getPersons()[1]).setFirstName(""); // violate NotEmpty
+        // constraint
+        violations = validator.validate(lib);
+        assertEquals(1, violations.size());
+        assertNotNull(TestUtils.getViolation(violations, "employees[1].firstName"));
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence.java
new file mode 100644
index 0000000..b2fa2bd
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence.java
@@ -0,0 +1,28 @@
+/*
+ * 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.bval.jsr303.groups;
+
+import javax.validation.GroupSequence;
+
+/**
+ * Description: <br/>
+ */
+@GroupSequence(value = CyclicGroupSequence.class)
+public interface CyclicGroupSequence {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence1.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence1.java
new file mode 100644
index 0000000..d976aab
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence1.java
@@ -0,0 +1,28 @@
+/*
+ * 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.bval.jsr303.groups;
+
+import javax.validation.GroupSequence;
+
+/**
+ * Description: <br/>
+ */
+@GroupSequence(value = CyclicGroupSequence2.class)
+public interface CyclicGroupSequence1 {
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence2.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence2.java
new file mode 100644
index 0000000..fdd2d36
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/CyclicGroupSequence2.java
@@ -0,0 +1,29 @@
+/*
+ * 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.bval.jsr303.groups;
+
+import javax.validation.GroupSequence;
+
+/**
+ * Description: <br/>
+ */
+@GroupSequence( value = CyclicGroupSequence1.class)
+public interface CyclicGroupSequence2 {
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/DefaultGroupSequenceTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/DefaultGroupSequenceTest.java
new file mode 100644
index 0000000..4b195ab
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/DefaultGroupSequenceTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.bval.jsr303.groups;
+
+import junit.framework.TestCase;
+
+import javax.validation.GroupDefinitionException;
+import javax.validation.groups.Default;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Hardy Ferentschik
+ * @author Roman Stumm
+ */
+public class DefaultGroupSequenceTest extends TestCase {
+    public void testAssertDefaultGroupSequenceIsExpandableWithDefaultAtEndOfSequence() {
+        // create a dummy sequence
+        Group a = new Group(GroupA.class);
+        Group b = new Group(GroupB.class);
+        Group c = new Group(GroupC.class);
+        Group defaultGroup = new Group(Default.class);
+        List<Group> sequence = new ArrayList<Group>();
+        sequence.add(a);
+        sequence.add(b);
+        sequence.add(c);
+        sequence.add(defaultGroup);
+
+        Groups chain = new Groups();
+        chain.insertSequence(sequence);
+
+        // create test default sequence
+        List<Group> defaultSequence = new ArrayList<Group>();
+        defaultSequence.add(Group.DEFAULT);
+        defaultSequence.add(new Group(GroupA.class));
+        try {
+            chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+            fail();
+        } catch (GroupDefinitionException e) {
+            // success
+        }
+
+        defaultSequence.clear();
+        defaultSequence.add(new Group(GroupA.class));
+        defaultSequence.add(new Group(Default.class));
+        try {
+            chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+            fail();
+        } catch (GroupDefinitionException e) {
+            // success
+        }
+
+        defaultSequence.clear();
+        defaultSequence.add(Group.DEFAULT);
+        defaultSequence.add(new Group(GroupC.class));
+        try {
+            chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+            fail();
+        } catch (GroupDefinitionException e) {
+            // success
+        }
+
+        defaultSequence.clear();
+        defaultSequence.add(new Group(GroupC.class));
+        defaultSequence.add(Group.DEFAULT);
+        chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+    }
+
+
+    public void testAssertDefaulGroupSequenceIsExpandableWithDefaultAtBeginningOfSequence() {
+        // create a dummy sequence
+        Group a = new Group(GroupA.class);
+        Group b = new Group(GroupB.class);
+        Group c = new Group(GroupC.class);
+        Group defaultGroup = new Group(Default.class);
+        List<Group> sequence = new ArrayList<Group>();
+        sequence.add(defaultGroup);
+        sequence.add(a);
+        sequence.add(b);
+        sequence.add(c);
+
+        Groups chain = new Groups();
+        chain.insertSequence(sequence);
+
+        // create test default sequence
+        List<Group> defaultSequence = new ArrayList<Group>();
+        defaultSequence.add(Group.DEFAULT);
+        defaultSequence.add(new Group(GroupA.class));
+        chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+
+
+        defaultSequence.clear();
+        defaultSequence.add(new Group(GroupA.class));
+        defaultSequence.add(Group.DEFAULT);
+        try {
+            chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+            fail();
+        } catch (GroupDefinitionException e) {
+            // success
+        }
+
+        defaultSequence.clear();
+        defaultSequence.add(Group.DEFAULT);
+        defaultSequence.add(new Group(GroupC.class));
+        try {
+            chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+            fail();
+        } catch (GroupDefinitionException e) {
+            // success
+        }
+
+        defaultSequence.clear();
+        defaultSequence.add(new Group(GroupC.class));
+        defaultSequence.add(Group.DEFAULT);
+        try {
+            chain.assertDefaultGroupSequenceIsExpandable(defaultSequence);
+            fail();
+        } catch (GroupDefinitionException e) {
+            // success
+        }
+    }
+}
+
+interface TestSequence {
+}
+
+interface GroupA {
+}
+
+interface GroupB {
+}
+
+interface GroupC {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass1.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass1.java
new file mode 100644
index 0000000..35316f8
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass1.java
@@ -0,0 +1,26 @@
+/*
+ * 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.bval.jsr303.groups;
+
+/**
+ * Description: <br/>
+ */
+public class GClass1 implements GInterface1 {
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass2.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass2.java
new file mode 100644
index 0000000..ce1de51
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass2.java
@@ -0,0 +1,28 @@
+/*
+ * 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.bval.jsr303.groups;
+
+import javax.validation.GroupSequence;
+
+/**
+ * Description: <br/>
+ */
+@GroupSequence({GClass1.class, GClass2.class})
+public class GClass2 extends GClass1 {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass3.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass3.java
new file mode 100644
index 0000000..4eb92c8
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GClass3.java
@@ -0,0 +1,28 @@
+/*
+ * 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.bval.jsr303.groups;
+
+import javax.validation.GroupSequence;
+
+/**
+ * Description: <br/>
+ */
+@GroupSequence({GClass3.class, GClass1.class})
+public class GClass3 {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GInterface1.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GInterface1.java
new file mode 100644
index 0000000..0f7c3f9
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GInterface1.java
@@ -0,0 +1,29 @@
+/*
+ * 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.bval.jsr303.groups;
+
+import javax.validation.GroupSequence;
+
+/**
+ * Description: <br/>
+ */
+@GroupSequence(GInterface1.class)
+public interface GInterface1 {
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceIsolationTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceIsolationTest.java
new file mode 100644
index 0000000..996a734
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceIsolationTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.bval.jsr303.groups;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.DefaultMessageInterpolator;
+
+import javax.validation.*;
+import javax.validation.constraints.NotNull;
+import javax.validation.groups.Default;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Additional tests to check the correct processing of {@link GroupSequence}s
+ * by the validator.
+ * 
+ * @author Carlos Vara
+ */
+public class GroupSequenceIsolationTest extends TestCase {
+    
+    static ValidatorFactory factory;
+
+    static {
+        factory = Validation.buildDefaultValidatorFactory();
+        ((DefaultMessageInterpolator)factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+    }
+
+    private Validator getValidator() {
+        return factory.getValidator();
+    }
+
+    
+    /**
+     * When validating the {@link Default} group in a bean whose class doesn't
+     * define a {@link GroupSequence}, all the classes in the hierarchy must be
+     * checked for group sequence definitions and they must be evaluated in
+     * order for the constraints defined on those classes.
+     */
+    public void testGroupSequencesInHierarchyClasses() {
+        Validator validator = getValidator();
+        
+        HolderWithNoGS h = new HolderWithNoGS();
+        Set<ConstraintViolation<HolderWithNoGS>> violations;
+        
+        violations = validator.validate(h);
+        Assert.assertEquals("Unexpected number of violations", 2, violations.size());
+        for ( ConstraintViolation<HolderWithNoGS> violation : violations ) {
+            boolean good = violation.getPropertyPath().toString().equals("a1");
+            good |= violation.getPropertyPath().toString().equals("b2");
+            Assert.assertTrue("Wrong constraint", good);
+        }
+
+        h.a1 = "good";
+        violations = validator.validate(h);
+        Assert.assertEquals("Unexpected number of violations", 2, violations.size());
+        for ( ConstraintViolation<HolderWithNoGS> violation : violations ) {
+            boolean good = violation.getPropertyPath().toString().equals("a2");
+            good |= violation.getPropertyPath().toString().equals("b2");
+            Assert.assertTrue("Wrong constraint", good);
+        }
+        
+        h.b2 = "good";
+        violations = validator.validate(h);
+        Assert.assertEquals("Unexpected number of violations", 2, violations.size());
+        for ( ConstraintViolation<HolderWithNoGS> violation : violations ) {
+            boolean good = violation.getPropertyPath().toString().equals("a2");
+            good |= violation.getPropertyPath().toString().equals("b1");
+            Assert.assertTrue("Wrong constraint", good);
+        }
+        
+        h.b1 = "good";
+        violations = validator.validate(h);
+        Assert.assertEquals("Unexpected number of violations", 1, violations.size());
+        for ( ConstraintViolation<HolderWithNoGS> violation : violations ) {
+            boolean good = violation.getPropertyPath().toString().equals("a2");
+            Assert.assertTrue("Wrong constraint", good);
+        }
+    }
+    
+    /**
+     * When validating the {@link Default} group in a bean whose class defines
+     * a group sequence, that group sequence is used for all the constraints.
+     */
+    public void testGroupSequenceOfBeanClass() {
+        Validator validator = getValidator();
+        
+        HolderWithGS h = new HolderWithGS();
+        Set<ConstraintViolation<HolderWithGS>> violations;
+        
+        violations = validator.validate(h);
+        Assert.assertEquals("Unexpected number of violations", 1, violations.size());
+        for ( ConstraintViolation<HolderWithGS> violation : violations ) {
+            boolean good = violation.getPropertyPath().toString().equals("a1");
+            Assert.assertTrue("Wrong constraint", good);
+        }
+        
+        h.a1 = "good";
+        violations = validator.validate(h);
+        Assert.assertEquals("Unexpected number of violations", 2, violations.size());
+        for ( ConstraintViolation<HolderWithGS> violation : violations ) {
+            boolean good = violation.getPropertyPath().toString().equals("a2");
+            good |= violation.getPropertyPath().toString().equals("b2");
+            Assert.assertTrue("Wrong constraint", good);
+        }
+        
+        h.a2 = "good";
+        h.b2 = "good";
+        violations = validator.validate(h);
+        Assert.assertEquals("Unexpected number of violations", 1, violations.size());
+        for ( ConstraintViolation<HolderWithGS> violation : violations ) {
+            boolean good = violation.getPropertyPath().toString().equals("b1");
+            Assert.assertTrue("Wrong constraint", good);
+        }
+    }
+    
+    @GroupSequence({GroupA1.class, A.class})
+    public static class A {
+        @NotNull(groups={GroupA1.class})
+        public String a1;
+        @NotNull
+        public String a2;
+    }
+    
+    public static interface GroupA1 {
+    }
+    
+    @GroupSequence({B.class, GroupB1.class})
+    public static class B extends A {
+        @NotNull(groups={GroupB1.class})
+        public String b1;
+        @NotNull
+        public String b2;
+    }
+    
+    public static interface GroupB1 {
+        
+    }
+    
+    // No group sequence definition
+    public static class HolderWithNoGS extends B {
+        
+    }
+    
+    @GroupSequence({GroupA1.class, HolderWithGS.class, GroupB1.class})
+    public static class HolderWithGS extends B {
+        
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceTest.java
new file mode 100644
index 0000000..0afcb38
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupSequenceTest.java
@@ -0,0 +1,211 @@
+/*
+ * 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.bval.jsr303.groups;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.DefaultMessageInterpolator;
+import org.apache.bval.jsr303.Jsr303Features;
+import org.apache.bval.jsr303.example.*;
+import org.apache.bval.jsr303.util.TestUtils;
+import org.apache.bval.model.MetaBean;
+
+import javax.validation.*;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Description: test of group sequence behavior<br/>
+ */
+public class GroupSequenceTest extends TestCase {
+
+    static ValidatorFactory factory;
+
+    static {
+        factory = Validation.buildDefaultValidatorFactory();
+        ((DefaultMessageInterpolator)factory.getMessageInterpolator()).setLocale(Locale.ENGLISH);
+    }
+
+    private Validator getValidator() {
+        return factory.getValidator();
+    }
+
+
+    public void testGroupSequence1() {
+        MetaBean metaBean =
+              ApacheValidatorFactory.getDefault().usingContext().getMetaBeanFinder()
+                    .findForClass(GInterface1.class);
+        List<Group> gseq = metaBean.getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
+        Assert.assertNotNull(gseq);
+        Assert.assertEquals(1, gseq.size());
+        Assert.assertEquals(Group.DEFAULT, gseq.get(0));
+    }
+
+    public void testGroupSequence2() {
+        MetaBean metaBean =
+              ApacheValidatorFactory.getDefault().usingContext().getMetaBeanFinder()
+                    .findForClass(GClass1.class);
+        List<Group> gseq = metaBean.getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
+        Assert.assertNotNull(gseq);
+        Assert.assertEquals(1, gseq.size());
+        Assert.assertEquals(Group.DEFAULT, gseq.get(0));
+    }
+
+    public void testGroupSequence3() {
+        MetaBean metaBean =
+              ApacheValidatorFactory.getDefault().usingContext().getMetaBeanFinder()
+                    .findForClass(GClass2.class);
+        List<Group> gseq = metaBean.getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
+        Assert.assertNotNull(gseq);
+        Assert.assertEquals(2, gseq.size());
+        Assert.assertEquals(new Group(GClass1.class), gseq.get(0));
+        Assert.assertEquals(Group.DEFAULT, gseq.get(1));
+    }
+
+    public void testGroupSequence4() {
+        MetaBean metaBean =
+              ApacheValidatorFactory.getDefault().usingContext().getMetaBeanFinder()
+                    .findForClass(GClass3.class);
+        List<Group> gseq = metaBean.getFeature(Jsr303Features.Bean.GROUP_SEQUENCE);
+        Assert.assertNotNull(gseq);
+        Assert.assertEquals(2, gseq.size());
+        Assert.assertEquals(Group.DEFAULT, gseq.get(0));
+        Assert.assertEquals(new Group(GClass1.class), gseq.get(1));
+    }
+
+    public void testGroups() {
+        Validator validator = getValidator();
+
+        Author author = new Author();
+        author.setLastName("");
+        author.setFirstName("");
+        Book book = new Book();
+        book.setTitle("");
+        book.setAuthor(author);
+
+        Set<ConstraintViolation<Book>> constraintViolations =
+              validator.validate(book, First.class, Second.class, Last.class);
+        assertEquals("Wrong number of constraints", 3, constraintViolations.size());
+        assertNotNull(TestUtils.getViolation(constraintViolations, "title"));
+        assertNotNull(TestUtils.getViolation(constraintViolations, "author.firstName"));
+        assertNotNull(TestUtils.getViolation(constraintViolations, "author.lastName"));
+
+        author.setFirstName("Gavin");
+        author.setLastName("King");
+
+        constraintViolations = validator.validate(book, First.class, Second.class, Last.class);
+        ConstraintViolation<?> constraintViolation = constraintViolations.iterator().next();
+        assertEquals(1, constraintViolations.size());
+        assertEquals("may not be empty", constraintViolation.getMessage());
+        assertEquals(book, constraintViolation.getRootBean());
+        assertEquals(book.getTitle(), constraintViolation.getInvalidValue());
+        assertEquals("title", constraintViolation.getPropertyPath().toString());
+
+        book.setTitle("My fault");
+        book.setSubtitle("confessions of a president - a book for a nice price");
+
+        constraintViolations = validator.validate(book, First.class, Second.class, Last.class);
+        assertEquals(1, constraintViolations.size());
+        constraintViolation = constraintViolations.iterator().next();
+        assertEquals("size must be between 0 and 30", constraintViolation.getMessage());
+        assertEquals(book, constraintViolation.getRootBean());
+        assertEquals(book.getSubtitle(), constraintViolation.getInvalidValue());
+        assertEquals("subtitle", constraintViolation.getPropertyPath().toString());
+
+        book.setSubtitle("Capitalism in crisis");
+        author.setCompany("1234567890ß9876543212578909876542245678987432");
+
+        constraintViolations = validator.validate(book);
+        constraintViolation = constraintViolations.iterator().next();
+        assertEquals(1, constraintViolations.size());
+        assertEquals("size must be between 0 and 40", constraintViolation.getMessage());
+        assertEquals(book, constraintViolation.getRootBean());
+        assertEquals(author.getCompany(), constraintViolation.getInvalidValue());
+        assertEquals("author.company", constraintViolation.getPropertyPath().toString());
+
+        author.setCompany("apache");
+
+        constraintViolations = validator.validate(book, First.class, Second.class, Last.class);
+        assertEquals(0, constraintViolations.size());
+    }
+
+    public void testGroupSequence() {
+        Validator validator = getValidator();
+
+        Author author = new Author();
+        author.setLastName("");
+        author.setFirstName("");
+        Book book = new Book();
+        book.setAuthor(author);
+
+        Set<ConstraintViolation<Book>> constraintViolations =
+              validator.validate(book, Book.All.class);
+        assertEquals(2, constraintViolations.size());
+
+        author.setFirstName("Kelvin");
+        author.setLastName("Cline");
+
+        constraintViolations = validator.validate(book, Book.All.class);
+        ConstraintViolation<?> constraintViolation = constraintViolations.iterator().next();
+        assertEquals(1, constraintViolations.size());
+        assertEquals("may not be null", constraintViolation.getMessage());
+        assertEquals(book, constraintViolation.getRootBean());
+        assertEquals(book.getTitle(), constraintViolation.getInvalidValue());
+        assertEquals("title", constraintViolation.getPropertyPath().toString());
+
+        book.setTitle("247307892430798789024389798789");
+        book.setSubtitle("f43u rlök fjöq3liu opiur ölw3kj rölkj d");
+
+        constraintViolations = validator.validate(book, Book.All.class);
+        assertEquals(1, constraintViolations.size());
+    }
+
+    
+    /**
+     * Check that when there is one constraint failure in one of the groups in
+     * a sequence, validation stops.
+     * JSR-303: 3.4.3
+     */
+    public void testValidationStopsWhenFailuresOnGroup() {
+        Validator validator = getValidator();
+        
+        // Validate Dummy with its redefined Default group
+        Set<ConstraintViolation<Dummy>> violations = validator.validate(new Dummy());
+        assertEquals("Only 1 violation expected", 1, violations.size());
+        ConstraintViolation<Dummy> violation = violations.iterator().next();
+        assertEquals("Group1 should be evaluated first", "field1", violation.getPropertyPath().toString());
+    }
+    
+    @GroupSequence({Dummy.Group1.class, Dummy.class})
+    public static class Dummy {
+        
+        @NotNull(groups=Group1.class)
+        public String field1;
+        
+        @NotNull
+        public String field2;
+        
+        interface Group1 {
+        }
+    }
+    
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupValidationTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupValidationTest.java
new file mode 100644
index 0000000..79712ba
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupValidationTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.bval.jsr303.groups;
+
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.util.TestUtils;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validator;
+import java.util.Set;
+
+/**
+ * Description: test features from spec chapter 3.4 group and group sequence<br/>
+ */
+public class GroupValidationTest extends TestCase {
+    private Validator validator;
+
+    protected void setUp() {
+        validator = ApacheValidatorFactory.getDefault().getValidator();
+    }
+
+    /**
+     * test spec: @NotNull on firstname and on lastname are validated when
+     * the Default group is validated.
+     */
+    public void testValidateFirstNameLastNameWithDefaultGroup() {
+        BillableUser user = new BillableUser();
+
+        Set<ConstraintViolation<BillableUser>> violations = validator.validate(user);
+        assertEquals(2, violations.size());
+        ConstraintViolation<?> violation = TestUtils.getViolation(violations, "firstname");
+        assertNotNull(violation);
+        assertEquals(user, violation.getRootBean());
+        violation = TestUtils.getViolation(violations, "lastname");
+        assertNotNull(violation);
+        assertEquals(user, violation.getRootBean());
+    }
+
+    /**
+     * test spec: @NotNull is checked on defaultCreditCard when either the
+     * Billable or BuyInOneClick group is validated.
+     */
+  public void testValidateDefaultCreditCardInBillableGroup() {
+        BillableUser user = new BillableUser();
+
+        Set<ConstraintViolation<BillableUser>> violations = validator.validate(user,
+              Billable.class);
+        assertEquals(1, violations.size());
+        ConstraintViolation<?> violation = TestUtils.getViolation(violations, "defaultCreditCard");
+        assertNotNull(violation);
+        assertEquals(user, violation.getRootBean());
+    }
+
+  public void testValidateDefaultCreditCardInBillableAndByInOneClickGroup() {
+        BillableUser user = new BillableUser();
+
+        Set<ConstraintViolation<BillableUser>> violations = validator.validate(user,
+              BuyInOneClick.class, Billable.class);
+        assertEquals(1, violations.size());
+        ConstraintViolation<?> violation = TestUtils.getViolation(violations, "defaultCreditCard");
+        assertNotNull(violation);
+        assertEquals(user, violation.getRootBean());
+    }
+
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupsComputerTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupsComputerTest.java
new file mode 100644
index 0000000..d393812
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/GroupsComputerTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.bval.jsr303.groups;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.jsr303.example.Address;
+import org.apache.bval.jsr303.example.First;
+import org.apache.bval.jsr303.example.Last;
+import org.apache.bval.jsr303.example.Second;
+
+import javax.validation.GroupDefinitionException;
+import javax.validation.ValidationException;
+import javax.validation.groups.Default;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * GroupListComputer Tester.
+ *
+ * @author <Authors name>
+ * @version 1.0
+ * @since <pre>04/09/2009</pre>
+ */
+public class GroupsComputerTest extends TestCase {
+    GroupsComputer groupsComputer;
+
+    public GroupsComputerTest(String name) {
+        super(name);
+    }
+
+    public void setUp() throws Exception {
+        super.setUp();
+        groupsComputer = new GroupsComputer();
+    }
+
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public static Test suite() {
+        return new TestSuite(GroupsComputerTest.class);
+    }
+
+    public void testComputeGroupsNotAnInterface() {
+        Set<Class<?>> groups = new HashSet<Class<?>>();
+        groups.add(String.class);
+        try {
+            groupsComputer.computeGroups(groups);
+            fail();
+        } catch (ValidationException ex) {
+
+        }
+    }
+
+    public void testGroupChainForNull() {
+        try {
+            groupsComputer.computeGroups((Class<?>[]) null);
+            fail();
+        } catch (IllegalArgumentException ex) {
+
+        }
+    }
+
+    public void testGroupChainForEmptySet() {
+        try {
+            groupsComputer.computeGroups(new HashSet<Class<?>>());
+            fail();
+        } catch (IllegalArgumentException ex) {
+
+        }
+    }
+
+    public void testCyclicGroupSequences() {
+        try {
+            Set<Class<?>> groups = new HashSet<Class<?>>();
+            groups.add(CyclicGroupSequence1.class);
+            groupsComputer.computeGroups(groups);
+            fail();
+        } catch (GroupDefinitionException ex) {
+
+        }
+    }
+
+    public void testCyclicGroupSequence() {
+        try {
+            Set<Class<?>> groups = new HashSet<Class<?>>();
+            groups.add(CyclicGroupSequence.class);
+            groupsComputer.computeGroups(groups);
+            fail();
+        } catch (GroupDefinitionException ex) {
+
+        }
+    }
+
+    public void testGroupDuplicates() {
+        Set<Class<?>> groups = new HashSet<Class<?>>();
+        groups.add(First.class);
+        groups.add(Second.class);
+        groups.add(Last.class);
+        Groups chain = groupsComputer.computeGroups(groups);
+        assertEquals(3, chain.groups.size());
+
+        groups.clear();
+        groups.add(First.class);
+        groups.add(First.class);
+        chain = groupsComputer.computeGroups(groups);
+        assertEquals(1, chain.groups.size());
+
+        groups.clear();
+        groups.add(First.class);
+        groups.add(Last.class);
+        groups.add(First.class);
+        chain = groupsComputer.computeGroups(groups);
+        assertEquals(2, chain.groups.size());
+    }
+
+
+    public void testSequenceResolution() {
+        Set<Class<?>> groups = new HashSet<Class<?>>();
+        groups.add(Address.Complete.class);
+        Groups chain = groupsComputer.computeGroups(groups);
+        Iterator<List<Group>> sequences = chain.getSequences().iterator();
+        List<Group> sequence = sequences.next();
+
+        assertEquals(Default.class, sequence.get(0).getGroup());
+        assertEquals(Address.HighLevelCoherence.class, sequence.get(1).getGroup());
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/Auditable.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/Auditable.java
new file mode 100644
index 0000000..75b0dca
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/Auditable.java
@@ -0,0 +1,33 @@
+/*
+ * 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.bval.jsr303.groups.implicit;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * Auditable object contract.
+ * Example 3.7. Example of interface / group hosting constraints
+ */
+public interface Auditable {
+    @NotNull String getCreationDate();
+    @NotNull
+    String getLastUpdate();
+    @NotNull String getLastModifier();
+    @NotNull String getLastReader();
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/ImplicitGroupingTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/ImplicitGroupingTest.java
new file mode 100644
index 0000000..c91afc5
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/ImplicitGroupingTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.bval.jsr303.groups.implicit;
+
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.util.TestUtils;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validator;
+import java.util.Set;
+
+/**
+ * Description: test spec chapter 3.4.4. Implicit grouping<br/>
+ */
+public class ImplicitGroupingTest extends TestCase {
+    private Validator validator;
+
+    protected void setUp() {
+        validator = ApacheValidatorFactory.getDefault().getValidator();
+    }
+
+    public void testValidateImplicitGrouping() {
+        Order order = new Order();
+        // When an Order object is validated on the Default group, ...
+        Set<ConstraintViolation<Order>> violations = validator.validate(order);
+        assertNotNull(TestUtils.getViolation(violations, "creationDate"));
+        assertNotNull(TestUtils.getViolation(violations, "lastUpdate"));
+        assertNotNull(TestUtils.getViolation(violations, "lastModifier"));
+        assertNotNull(TestUtils.getViolation(violations, "lastReader"));
+        assertNotNull(TestUtils.getViolation(violations, "orderNumber"));
+        assertEquals(5, violations.size());
+
+        // When an Order object is validated on the Auditable group, ...
+
+        /* Only the constraints present on Auditable (and any of its super interfaces)
+           and belonging to the Default group are validated
+           when the group Auditable is requested. */
+        violations = validator.validate(order, Auditable.class);
+        assertEquals("Implicit grouping not correctly implemented", 4, violations.size());
+        assertNotNull(TestUtils.getViolation(violations, "creationDate"));
+        assertNotNull(TestUtils.getViolation(violations, "lastUpdate"));
+        assertNotNull(TestUtils.getViolation(violations, "lastModifier"));
+        assertNotNull(TestUtils.getViolation(violations, "lastReader"));
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/Order.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/Order.java
new file mode 100644
index 0000000..fb6e7f8
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/implicit/Order.java
@@ -0,0 +1,76 @@
+/*
+ * 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.bval.jsr303.groups.implicit;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+/**
+ * Represents an order in the system
+ */
+public class Order implements Auditable {
+    private String creationDate;
+    private String lastUpdate;
+    private String lastModifier;
+    private String lastReader;
+
+    private String orderNumber;
+
+    public String getCreationDate() {
+        return this.creationDate;
+    }
+
+    public String getLastUpdate() {
+        return this.lastUpdate;
+    }
+
+    public String getLastModifier() {
+        return this.lastModifier;
+    }
+
+    public String getLastReader() {
+        return this.lastReader;
+    }
+
+    @NotNull
+    @Size(min=10, max=10)
+    public String getOrderNumber() {
+        return this.orderNumber;
+    }
+
+    public void setCreationDate(String creationDate) {
+        this.creationDate = creationDate;
+    }
+
+    public void setLastUpdate(String lastUpdate) {
+        this.lastUpdate = lastUpdate;
+    }
+
+    public void setLastModifier(String lastModifier) {
+        this.lastModifier = lastModifier;
+    }
+
+    public void setLastReader(String lastReader) {
+        this.lastReader = lastReader;
+    }
+
+    public void setOrderNumber(String orderNumber) {
+        this.orderNumber = orderNumber;
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/BillableUser.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/BillableUser.java
new file mode 100644
index 0000000..04e9af5
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/BillableUser.java
@@ -0,0 +1,64 @@
+/*
+ * 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.bval.jsr303.groups.inheritance;
+
+
+import org.apache.bval.jsr303.groups.Billable;
+import org.apache.bval.jsr303.groups.BillableCreditCard;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.groups.Default;
+
+/**
+ * Description: <br/>
+ */
+public class BillableUser {
+    @NotNull
+    private String firstname;
+
+    @NotNull(groups = Default.class)
+    private String lastname;
+
+    @NotNull(groups = {Billable.class})
+    private BillableCreditCard defaultCreditCard;
+
+    public String getFirstname() {
+        return firstname;
+    }
+
+    public void setFirstname(String firstname) {
+        this.firstname = firstname;
+    }
+
+    public String getLastname() {
+        return lastname;
+    }
+
+    public void setLastname(String lastname) {
+        this.lastname = lastname;
+    }
+
+    public BillableCreditCard getDefaultCreditCard() {
+        return defaultCreditCard;
+    }
+
+    public void setDefaultCreditCard(BillableCreditCard defaultCreditCard) {
+        this.defaultCreditCard = defaultCreditCard;
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/BuyInOneClick.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/BuyInOneClick.java
new file mode 100644
index 0000000..744ad59
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/BuyInOneClick.java
@@ -0,0 +1,31 @@
+/*
+ * 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.bval.jsr303.groups.inheritance;
+
+
+import org.apache.bval.jsr303.groups.Billable;
+
+import javax.validation.groups.Default;
+
+/**
+ * Customer can buy without harrassing checking process.
+ * spec: Example 3.3. Groups can inherit other groups
+ */
+public interface BuyInOneClick extends Default, Billable {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/GroupInheritanceTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/GroupInheritanceTest.java
new file mode 100644
index 0000000..01a81b2
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/inheritance/GroupInheritanceTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.bval.jsr303.groups.inheritance;
+
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.util.TestUtils;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validator;
+import java.util.Set;
+
+/**
+ * Description: <br/>
+ */
+public class GroupInheritanceTest extends TestCase {
+    private Validator validator;
+
+    protected void setUp() {
+        validator = ApacheValidatorFactory.getDefault().getValidator();
+    }
+
+    /**
+     * validating the group BuyInOneClick will lead to the following constraints checking:
+     *<pre>
+     *  * @NotNull on firstname and lastname
+     *  * @NotNull on defaultCreditCard</pre>
+     * because Default and Billable are superinterfaces of BuyInOneClick.
+     */
+    public void testValidGroupBuyInOneClick() {
+        BillableUser user = new BillableUser();
+
+        Set<ConstraintViolation<BillableUser>> violations =
+              validator.validate(user, BuyInOneClick.class);
+        assertEquals(3, violations.size());
+        assertNotNull(TestUtils.getViolation(violations, "firstname"));
+        assertNotNull(TestUtils.getViolation(violations, "lastname"));
+        assertNotNull(TestUtils.getViolation(violations, "defaultCreditCard"));
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/Address.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/Address.java
new file mode 100644
index 0000000..e20cade
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/Address.java
@@ -0,0 +1,84 @@
+/*
+ * 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.bval.jsr303.groups.redefining;
+
+
+import org.apache.bval.constraints.ZipCodeCityCoherence;
+import org.apache.bval.jsr303.example.ZipCodeCityCarrier;
+
+import javax.validation.GroupSequence;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+/**
+ * Example 3.6. Redefining Default group for Address:
+ * To redefine Default for a class, place a @GroupSequence annotation on the class ; 
+ * this sequence expresses the sequence of groups that does
+ * substitute Default for this class.
+ */
+@GroupSequence({Address.class, Address.HighLevelCoherence.class, Address.ExtraCareful.class })
+@ZipCodeCityCoherence(groups = Address.HighLevelCoherence.class)
+public class Address implements ZipCodeCityCarrier {
+    
+    /**
+     * check coherence on the overall object
+     * Needs basic checking to be green first
+     */
+    public interface HighLevelCoherence {}
+    
+    /**
+     * Extra-careful validation group.
+     */
+    public interface ExtraCareful {}
+
+    @NotNull
+    @Size(max = 50, min = 1, groups = ExtraCareful.class)
+    private String street1;
+
+    @NotNull 
+    private String zipCode;
+    
+    @NotNull
+    @Size(max = 30)
+    private String city;
+
+    public String getStreet1() {
+        return street1;
+    }
+
+    public void setStreet1(String street1) {
+        this.street1 = street1;
+    }
+
+    public String getZipCode() {
+        return zipCode;
+    }
+
+    public void setZipCode(String zipCode) {
+        this.zipCode = zipCode;
+    }
+
+    public String getCity() {
+        return city;
+    }
+
+    public void setCity(String city) {
+        this.city = city;
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/InvalidRedefinedDefaultGroupAddress.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/InvalidRedefinedDefaultGroupAddress.java
new file mode 100644
index 0000000..162a811
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/InvalidRedefinedDefaultGroupAddress.java
@@ -0,0 +1,46 @@
+/*
+ * 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.bval.jsr303.groups.redefining;
+
+import javax.validation.GroupSequence;
+import javax.validation.constraints.NotNull;
+
+
+/**
+ * If a @GroupSequence redefining the Default group for a class A does not
+ * contain the group A, a GroupDefinitionException is raised when the class is
+ * validated or when its metadata is requested.
+ */
+@GroupSequence({Address.class, Address.HighLevelCoherence.class})
+public class InvalidRedefinedDefaultGroupAddress {
+    @SuppressWarnings("unused")
+    @NotNull(groups = Address.HighLevelCoherence.class)
+    private String street;
+
+    @SuppressWarnings("unused")
+    @NotNull
+    private String city;
+
+    /**
+     * check coherence on the overall object
+     * Needs basic checking to be green first
+     */
+    public interface HighLevelCoherence {}
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/RedefiningDefaultGroupTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/RedefiningDefaultGroupTest.java
new file mode 100644
index 0000000..9295985
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/groups/redefining/RedefiningDefaultGroupTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.bval.jsr303.groups.redefining;
+
+import junit.framework.TestCase;
+import org.apache.bval.jsr303.ApacheValidatorFactory;
+import org.apache.bval.jsr303.util.TestUtils;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.GroupDefinitionException;
+import javax.validation.Validator;
+import java.util.Set;
+
+/**
+ * Description: test Redefining the Default group for a class (spec. chapter 3.4.3)<br/>
+ */
+public class RedefiningDefaultGroupTest extends TestCase {
+    private Validator validator;
+
+    protected void setUp() {
+        validator = ApacheValidatorFactory.getDefault().getValidator();
+    }
+
+    /**
+     * when an address object is validated for the group Default,
+     * all constraints belonging to the group Default and hosted on Address are evaluated
+     */
+    public void testValidateDefaultGroup() {
+        Address address = new Address();
+        Set<ConstraintViolation<Address>> violations = validator.validate(address);
+        assertEquals(3, violations.size());
+        assertNotNull(TestUtils.getViolation(violations, "street1"));
+        assertNotNull(TestUtils.getViolation(violations, "zipCode"));
+        assertNotNull(TestUtils.getViolation(violations, "city"));
+
+        address.setStreet1("Elmstreet");
+        address.setZipCode("1234");
+        address.setCity("Gotham City");
+        violations = validator.validate(address);
+        assertTrue(violations.isEmpty());
+
+        violations = validator.validate(address, Address.HighLevelCoherence.class);
+        assertEquals(0, violations.size());
+
+        address.setCity("error");
+        violations = validator.validate(address, Address.HighLevelCoherence.class);
+        assertEquals(1, violations.size());
+
+        /**
+         * If none fails, all HighLevelCoherence constraints present on Address are evaluated.
+         *
+         * In other words, when validating the Default group for Address,
+         * the group sequence defined on the Address class is used.
+         */
+        violations = validator.validate(address);
+        assertEquals(
+              "redefined default group for Address must also validate HighLevelCoherence",
+              1, violations.size());
+    }
+
+    public void testValidateProperty() {
+        Address address = new Address();
+        address.setStreet1("");
+        Set<ConstraintViolation<Address>> violations = validator.validateProperty(address, "street1");
+        //prove that ExtraCareful group was validated:
+        assertEquals(1, violations.size());
+        assertNotNull(TestUtils.getViolation(violations, "street1"));
+    }
+
+    public void testValidateValue() {
+        Set<ConstraintViolation<Address>> violations = validator.validateValue(Address.class, "street1", "");
+        //prove that ExtraCareful group was validated:
+        assertEquals(1, violations.size());
+        assertNotNull(TestUtils.getViolation(violations, "street1"));
+    }
+    
+    public void testRaiseGroupDefinitionException() {
+        InvalidRedefinedDefaultGroupAddress address =
+              new InvalidRedefinedDefaultGroupAddress();
+        try {
+            validator.validate(address);
+            fail();
+        } catch (GroupDefinitionException ex) {
+
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/EnumerationConverterTestCase.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/EnumerationConverterTestCase.java
new file mode 100644
index 0000000..7bc3ecb
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/EnumerationConverterTestCase.java
@@ -0,0 +1,50 @@
+/*
+ * 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.bval.jsr303.util;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.commons.beanutils.Converter;
+
+/**
+ * EnumerationConverter tester.
+ *
+ * $Id$
+ */
+public final class EnumerationConverterTestCase extends TestCase {
+
+    public EnumerationConverterTestCase(String name) {
+        super(name);
+    }
+
+    public void testEnum() {
+        Converter converter = EnumerationConverter.getInstance();
+
+        Thread.State expected = Thread.State.TERMINATED;
+        Thread.State actual = (Thread.State) converter.convert(Thread.State.class,
+                Thread.State.TERMINATED.name());
+        assertEquals(expected, actual);
+    }
+
+    public static Test suite() {
+        return new TestSuite(EnumerationConverterTestCase.class);
+    }
+
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/PathImplTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/PathImplTest.java
new file mode 100644
index 0000000..fbd60fe
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/PathImplTest.java
@@ -0,0 +1,210 @@
+/*
+ * 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.bval.jsr303.util;
+
+import java.util.Iterator;
+
+import javax.validation.Path;
+import javax.validation.ValidationException;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * PathImpl Tester.
+ *
+ * @version 1.0
+ * @since <pre>10/01/2009</pre>
+ */
+public class PathImplTest extends TestCase {
+    public PathImplTest(String name) {
+        super(name);
+    }
+
+    public void testParsing() {
+        String property = "order[3].deliveryAddress.addressline[1]";
+        Path path = PathImpl.createPathFromString(property);
+        assertEquals(property, path.toString());
+
+        Iterator<Path.Node> propIter = path.iterator();
+
+        assertTrue(propIter.hasNext());
+        Path.Node elem = propIter.next();
+        assertFalse(elem.isInIterable());
+        assertEquals("order", elem.getName());
+
+        assertTrue(propIter.hasNext());
+        elem = propIter.next();
+        assertTrue(elem.isInIterable());
+        assertEquals(new Integer(3), elem.getIndex());
+        assertEquals("deliveryAddress", elem.getName());
+
+        assertTrue(propIter.hasNext());
+        elem = propIter.next();
+        assertFalse(elem.isInIterable());
+        assertEquals(null, elem.getIndex());
+        assertEquals("addressline", elem.getName());
+
+        assertTrue(propIter.hasNext());
+        elem = propIter.next();
+        assertTrue(elem.isInIterable());
+        assertEquals(new Integer(1), elem.getIndex());
+        assertNull(elem.getName());
+
+        assertFalse(propIter.hasNext());
+    }
+
+    public void testParseMapBasedProperty() {
+        String property = "order[foo].deliveryAddress";
+        Path path = PathImpl.createPathFromString(property);
+        Iterator<Path.Node> propIter = path.iterator();
+
+        assertTrue(propIter.hasNext());
+        Path.Node elem = propIter.next();
+        assertFalse(elem.isInIterable());
+        assertEquals("order", elem.getName());
+
+        assertTrue(propIter.hasNext());
+        elem = propIter.next();
+        assertTrue(elem.isInIterable());
+        assertEquals("foo", elem.getKey());
+        assertEquals("deliveryAddress", elem.getName());
+
+        assertFalse(propIter.hasNext());
+    }
+
+    //some of the examples from the 1.0 bean validation spec, section 4.2
+    public void testSpecExamples() {
+        String fourthAuthor = "authors[3]";
+        Path path = PathImpl.createPathFromString(fourthAuthor);
+        Iterator<Path.Node> propIter = path.iterator();
+
+        assertTrue(propIter.hasNext());
+        Path.Node elem = propIter.next();
+        assertFalse(elem.isInIterable());
+        assertEquals("authors", elem.getName());
+
+        assertTrue(propIter.hasNext());
+        elem = propIter.next();
+        assertTrue(elem.isInIterable());
+        assertEquals(3, elem.getIndex().intValue());
+        assertNull(elem.getName());
+        assertFalse(propIter.hasNext());
+
+        String firstAuthorCompany = "authors[0].company";
+        path = PathImpl.createPathFromString(firstAuthorCompany);
+        propIter = path.iterator();
+
+        assertTrue(propIter.hasNext());
+        elem = propIter.next();
+        assertFalse(elem.isInIterable());
+        assertEquals("authors", elem.getName());
+
+        assertTrue(propIter.hasNext());
+        elem = propIter.next();
+        assertTrue(elem.isInIterable());
+        assertEquals(0, elem.getIndex().intValue());
+        assertEquals("company", elem.getName());
+        assertFalse(propIter.hasNext());
+    }
+
+    public void testNull() {
+        assertEquals(PathImpl.createPathFromString(null), PathImpl.create(null));
+
+        assertEquals("", PathImpl.create(null).toString());
+        Path path = PathImpl.create(null);
+        Path.Node node = path.iterator().next();
+        assertEquals(null, node.getName());
+    }
+
+    public void testUnbalancedBraces() {
+        try {
+            PathImpl.createPathFromString("foo[.bar");
+            fail();
+        } catch (ValidationException ex) {
+        }
+    }
+
+    public void testIndexInMiddleOfProperty() {
+        try {
+            PathImpl.createPathFromString("f[1]oo.bar");
+            fail();
+        } catch (ValidationException ex) {
+        }
+    }
+
+    public void testTrailingPathSeparator() {
+        try {
+            PathImpl.createPathFromString("foo.bar.");
+            fail();
+        } catch (ValidationException ex) {
+        }
+    }
+
+    public void testLeadingPathSeparator() {
+        try {
+            PathImpl.createPathFromString(".foo.bar");
+            fail();
+        } catch (ValidationException ex) {
+        }
+    }
+
+    public void testEmptyString() {
+        Path path = PathImpl.createPathFromString("");
+        assertEquals(null, path.iterator().next().getName());
+    }
+
+    public void testToString() {
+        PathImpl path = PathImpl.create(null);
+        path.addNode(new NodeImpl("firstName"));
+        assertEquals("firstName", path.toString());
+
+        path = PathImpl.create(null);
+        path.getLeafNode().setIndex(2);
+        assertEquals("[2]", path.toString());
+        path.addNode(new NodeImpl("firstName"));
+        assertEquals("[2].firstName", path.toString());
+    }
+
+    public void testAddRemoveNodes() {
+        PathImpl path = PathImpl.createPathFromString("");
+        assertTrue(path.isRootPath());
+        assertEquals(1, countNodes(path));
+        path.addNode(new NodeImpl("foo"));
+        assertFalse(path.isRootPath());
+        assertEquals(1, countNodes(path));
+        path.removeLeafNode();
+        assertTrue(path.isRootPath());
+        assertEquals(1, countNodes(path));
+    }
+
+    private int countNodes(Path path) {
+        int result = 0;
+        for (Iterator<Path.Node> iter = path.iterator(); iter.hasNext();) {
+            iter.next();
+            result++;
+        }
+        return result;
+    }
+
+    public static Test suite() {
+        return new TestSuite(PathImplTest.class);
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/TestUtils.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/TestUtils.java
new file mode 100644
index 0000000..30fcb4b
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/util/TestUtils.java
@@ -0,0 +1,113 @@
+/*
+ * 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.bval.jsr303.util;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.metadata.ConstraintDescriptor;
+import javax.validation.metadata.ElementDescriptor.ConstraintFinder;
+
+import junit.framework.Assert;
+
+/**
+ * Description: <br/>
+ */
+public class TestUtils {
+    /**
+     * @param violations
+     * @param propertyPath
+     *            - string format of a propertyPath
+     * @return the constraintViolation with the propertyPath's string
+     *         representation given
+     */
+    public static <T> ConstraintViolation<T> getViolation(Set<ConstraintViolation<T>> violations, String propertyPath) {
+        for (ConstraintViolation<T> each : violations) {
+            if (each.getPropertyPath().toString().equals(propertyPath))
+                return each;
+        }
+        return null;
+    }
+
+    /**
+     * @param violations
+     * @param propertyPath
+     * @return count of violations
+     */
+    public static <T> int countViolations(Set<ConstraintViolation<T>> violations, String propertyPath) {
+        int result = 0;
+        for (ConstraintViolation<T> each : violations) {
+            if (each.getPropertyPath().toString().equals(propertyPath)) {
+                result++;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @param <T>
+     * @param violations
+     * @param message
+     * @return the constraint violation with the specified message found, if any
+     */
+    public static <T> ConstraintViolation<T> getViolationWithMessage(Set<ConstraintViolation<T>> violations,
+        String message) {
+        for (ConstraintViolation<T> each : violations) {
+            if (each.getMessage().equals(message))
+                return each;
+        }
+        return null;
+    }
+
+    /**
+     * assume set addition either does nothing, returning false per collection
+     * contract, or throws an Exception; in either case size should remain
+     * unchanged
+     * 
+     * @param collection
+     */
+    public static void failOnModifiable(Collection<?> collection, String description) {
+        int size = collection.size();
+        try {
+            Assert
+                .assertFalse(String.format("should not permit modification to %s", description), collection.add(null));
+        } catch (Exception e) {
+            // okay
+        }
+        Assert.assertEquals("constraint descriptor set size changed", size, collection.size());
+    }
+
+    /**
+     * Assert that the specified ConstraintFinder provides constraints of each of the specified types.
+     * @param constraintFinder
+     * @param types
+     */
+    public static void assertConstraintTypesFound(ConstraintFinder constraintFinder, Class<? extends Annotation>... types) {
+        outer: for (Class<? extends Annotation> type : types) {
+            for (ConstraintDescriptor<?> descriptor : constraintFinder.getConstraintDescriptors()) {
+                if (descriptor.getAnnotation().annotationType().equals(type)) {
+                    continue outer;
+                }
+            }
+            Assert.fail(String.format("Missing expected constraint descriptor of type %s", type));
+        }
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/TestConstraintValidatorFactory.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/TestConstraintValidatorFactory.java
new file mode 100644
index 0000000..b05f5ea
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/TestConstraintValidatorFactory.java
@@ -0,0 +1,27 @@
+/*
+ * 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.bval.jsr303.xml;
+
+import org.apache.bval.jsr303.DefaultConstraintValidatorFactory;
+
+/**
+ * Description: <br/>
+ */
+public class TestConstraintValidatorFactory extends DefaultConstraintValidatorFactory {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/TestMessageInterpolator.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/TestMessageInterpolator.java
new file mode 100644
index 0000000..8c4418a
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/TestMessageInterpolator.java
@@ -0,0 +1,27 @@
+/*
+ * 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.bval.jsr303.xml;
+
+import org.apache.bval.jsr303.DefaultMessageInterpolator;
+
+/**
+ * Description: <br/>
+ */
+public class TestMessageInterpolator extends DefaultMessageInterpolator {
+}
diff --git a/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/ValidationParserTest.java b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/ValidationParserTest.java
new file mode 100644
index 0000000..b153b77
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/java/org/apache/bval/jsr303/xml/ValidationParserTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.bval.jsr303.xml;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.jsr303.ApacheValidationProvider;
+import org.apache.bval.jsr303.ApacheValidatorConfiguration;
+import org.apache.bval.jsr303.ConfigurationImpl;
+import org.apache.bval.jsr303.example.XmlEntitySampleBean;
+import org.apache.bval.jsr303.resolver.SimpleTraversableResolver;
+
+import javax.validation.*;
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * ValidationParser Tester.
+ *
+ * @author <Authors name>
+ * @version 1.0
+ * @since <pre>11/25/2009</pre>
+ */
+public class ValidationParserTest extends TestCase
+      implements ApacheValidatorConfiguration.Properties {
+    public ValidationParserTest(String name) {
+        super(name);
+    }
+
+    public void testGetInputStream() throws IOException {
+        ValidationParser vp = new ValidationParser("sample-validation.xml");
+        assertNotNull(vp.getInputStream("sample-validation.xml"));
+
+        try {
+            vp.getInputStream("META-INF/MANIFEST.MF"); // this is available in multiple jars hopefully
+            fail("exception not thrown");
+        } catch(ValidationException vex) {
+            assertTrue(vex.getMessage().startsWith("More than "));
+        }
+    }
+
+    public void testParse() {
+        ValidationParser vp = new ValidationParser("sample-validation.xml");
+        ConfigurationImpl config =
+              new ConfigurationImpl(null, new ApacheValidationProvider());
+        vp.processValidationConfig(config);
+    }
+
+    public void testConfigureFromXml() {
+        ValidatorFactory factory = getFactory();
+        assertTrue(factory.getMessageInterpolator() instanceof TestMessageInterpolator);
+        assertTrue(factory
+              .getConstraintValidatorFactory() instanceof TestConstraintValidatorFactory);
+        assertTrue(factory.getTraversableResolver() instanceof SimpleTraversableResolver);
+        Validator validator = factory.getValidator();
+        assertNotNull(validator);
+    }
+
+    private ValidatorFactory getFactory() {
+        ApacheValidatorConfiguration config =
+              Validation.byProvider(ApacheValidationProvider.class).configure();
+        config.addProperty(VALIDATION_XML_PATH, "sample-validation.xml");
+        return config.buildValidatorFactory();
+    }
+
+    public void testXmlEntitySample() {
+        XmlEntitySampleBean bean = new XmlEntitySampleBean();
+        bean.setFirstName("tooooooooooooooooooooooooooo long");
+        bean.setValueCode("illegal");
+        Validator validator = getFactory().getValidator();
+        Set<ConstraintViolation<XmlEntitySampleBean>> results = validator.validate(bean);
+        assertTrue(!results.isEmpty());
+        assertTrue(results.size() == 3);
+
+        bean.setZipCode("123");
+        bean.setValueCode("20");
+        bean.setFirstName("valid");
+        results = validator.validate(bean);
+        assertTrue(results.isEmpty());
+    }
+
+    public static Test suite() {
+        return new TestSuite(ValidationParserTest.class);
+    }
+}
diff --git a/trunk/bval-jsr303/src/test/resources/ValidationMessages.properties b/trunk/bval-jsr303/src/test/resources/ValidationMessages.properties
new file mode 100644
index 0000000..aad269b
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/resources/ValidationMessages.properties
@@ -0,0 +1,26 @@
+# 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.
+
+# standard messages
+javax.validation.constraints.Pattern.message=must match "{regexp}"
+
+# custom messages (examples) for validation-api-1.0.Beta4
+test.validator.creditcard=credit card is not valid
+
+# custom messages (examples) for validation-api-1.0.CR1
+validator.creditcard=credit card is not valid
+
diff --git a/trunk/bval-jsr303/src/test/resources/java.policy b/trunk/bval-jsr303/src/test/resources/java.policy
new file mode 100644
index 0000000..98bb345
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/resources/java.policy
@@ -0,0 +1,76 @@
+//

+// 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.

+//

+

+//

+// $Id$

+//

+// Allows unit tests to run with a Java Security Manager

+//

+// Cloned from https://svn.apache.org/repos/asf/commons/proper/lang/trunk/src/test/resources/java.policy

+//

+// <argLine>-Djava.security.manager -Djava.security.policy=${basedir}/src/test/resources/java.policy</argLine>

+//

+

+grant

+{

+  // let everyone read target dir

+  permission java.io.FilePermission "${user.dir}/target/-", "read";

+};

+

+// we don't care about the permissions of the testing infrastructure,

+// including maven;

+grant codeBase "file://${user.home}/.m2/repository/org/apache/maven/-"

+{

+  permission java.security.AllPermission;

+};

+

+// junit;

+grant codeBase "file://${user.home}/.m2/repository/junit/-"

+{

+  permission java.security.AllPermission;

+};

+

+// mockito;

+grant codeBase "file://${user.home}/.m2/repository/org/mockito/-"

+{

+  permission java.security.AllPermission;

+};

+

+// objenesis (via mockito);

+grant codeBase "file://${user.home}/.m2/repository/org/objenesis/-"

+{

+  permission java.security.AllPermission;

+};

+

+// and our own testcases

+grant codeBase "file://${user.dir}/target/test-classes/-"

+{

+  permission java.security.AllPermission;

+};

+

+grant codeBase "file://${user.home}/.m2/repository/org/apache/bval/-"

+{

+  permission java.lang.RuntimePermission "accessDeclaredMembers";

+  permission java.lang.reflect.ReflectPermission "suppressAccessChecks";

+};

+

+grant codeBase "file://${user.dir}/target/classes/-"

+{

+  permission java.lang.RuntimePermission "accessDeclaredMembers";

+  permission java.lang.reflect.ReflectPermission "suppressAccessChecks";

+  permission java.io.FilePermission "${user.home}/.m2/repository/-", "read";

+};

diff --git a/trunk/bval-jsr303/src/test/resources/sample-constraints.xml b/trunk/bval-jsr303/src/test/resources/sample-constraints.xml
new file mode 100644
index 0000000..75c405e
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/resources/sample-constraints.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<constraint-mappings
+    xmlns="http://jboss.org/xml/ns/javax/validation/mapping"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation=
+        "http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd">
+  <default-package>org.apache.bval.jsr303.example</default-package>
+
+  <bean class="XmlEntitySampleBean" ignore-annotations="false">
+    <class ignore-annotations="true"/>
+    <field name="zipCode">
+      <!--@FrenchZipCode(size=3)-->
+      <constraint annotation="org.apache.bval.constraints.FrenchZipCode">
+        <element name="size">
+          <value>3</value>
+        </element>
+      </constraint>
+
+    </field>
+    <field name="valueCode">
+      <valid/>
+      <!-- @HasValue({ 0, 20 }) -->
+      <constraint annotation="org.apache.bval.constraints.HasValue">
+        <element name="value">
+          <value>0</value>
+          <value>20</value>
+        </element>
+      </constraint>
+
+    </field>
+    <getter name="firstName">
+      <valid/>
+      <!-- @Size(message="Size is limited",
+                 groups={First.class, Default.class},
+                 max=10
+           )
+      -->
+      <constraint annotation="javax.validation.constraints.Size">
+        <message>Size is limited</message>
+        <groups>
+          <value>org.apache.bval.jsr303.example.First</value>
+          <value>javax.validation.groups.Default</value>
+        </groups>
+        <element name="max">10</element>
+      </constraint>
+
+    </getter>
+
+  </bean>
+
+  <constraint-definition annotation="javax.validation.constraints.Size">
+      <validated-by include-existing-validators="false">
+          <value>org.apache.bval.constraints.SizeValidatorForString</value>
+      </validated-by>
+  </constraint-definition>
+
+</constraint-mappings>
diff --git a/trunk/bval-jsr303/src/test/resources/sample-validation.xml b/trunk/bval-jsr303/src/test/resources/sample-validation.xml
new file mode 100644
index 0000000..790a535
--- /dev/null
+++ b/trunk/bval-jsr303/src/test/resources/sample-validation.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<validation-config
+    xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation=
+        "http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.0.xsd">
+  <default-provider>org.apache.bval.jsr303.ApacheValidationProvider</default-provider>
+  <message-interpolator>org.apache.bval.jsr303.xml.TestMessageInterpolator</message-interpolator>
+  <traversable-resolver>org.apache.bval.jsr303.resolver.SimpleTraversableResolver</traversable-resolver>
+  <constraint-validator-factory>org.apache.bval.jsr303.xml.TestConstraintValidatorFactory</constraint-validator-factory>
+  <constraint-mapping>sample-constraints.xml</constraint-mapping>
+  <property name="test-prop">test-prop-value</property>
+</validation-config>
diff --git a/trunk/bval-tck/pom.xml b/trunk/bval-tck/pom.xml
new file mode 100644
index 0000000..60513fe
--- /dev/null
+++ b/trunk/bval-tck/pom.xml
@@ -0,0 +1,416 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.bval</groupId>
+        <artifactId>bval-parent</artifactId>
+        <version>0.4</version>
+    </parent>
+
+    <artifactId>bval-tck-runner</artifactId>
+    <name>Apache BVal :: bval-tck (TCK Runner)</name>
+    <description>Aggregates dependencies and runs the JSR-303 TCK</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.bval</groupId>
+            <artifactId>org.apache.bval.bundle</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <properties>
+        <jboss.home>/opt/jboss-5.1.0.GA</jboss.home>
+        <validation.provider>org.apache.bval.jsr303.ApacheValidationProvider</validation.provider>
+        <remote.debug />
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <!-- Don't publish these artifacts to central -->
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+        <pluginManagement>
+            <plugins>
+                <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
+                <plugin>
+                    <groupId>org.eclipse.m2e</groupId>
+                    <artifactId>lifecycle-mapping</artifactId>
+                    <version>1.0.0</version>
+                    <configuration>
+                        <lifecycleMappingMetadata>
+                            <pluginExecutions>
+                                <pluginExecution>
+                                    <pluginExecutionFilter>
+                                        <groupId>
+                                            org.apache.maven.plugins
+                                        </groupId>
+                                        <artifactId>
+                                            maven-dependency-plugin
+                                        </artifactId>
+                                        <versionRange>
+                                            [2.1,)
+                                        </versionRange>
+                                        <goals>
+                                            <goal>copy</goal>
+                                        </goals>
+                                    </pluginExecutionFilter>
+                                    <action>
+                                        <ignore />
+                                    </action>
+                                </pluginExecution>
+                            </pluginExecutions>
+                        </lifecycleMappingMetadata>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
+
+    <profiles>
+        <profile>
+            <id>tck</id>
+            <activation>
+                <property>
+                    <name>tck</name>
+                </property>
+            </activation>
+            <dependencies>
+                <!-- Use the geronimo-validation spec instead
+                <dependency>
+                    <groupId>javax.validation</groupId>
+                    <artifactId>validation-api</artifactId>
+                </dependency>
+                -->
+                <dependency>
+                    <groupId>org.apache.geronimo.specs</groupId>
+                    <artifactId>geronimo-validation_1.0_spec</artifactId>
+                </dependency>
+                <dependency>
+                    <groupId>org.testng</groupId>
+                    <artifactId>testng</artifactId>
+                    <classifier>jdk15</classifier>
+                    <version>5.8</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.hibernate.jsr303.tck</groupId>
+                    <artifactId>jsr303-tck</artifactId>
+                    <version>1.0.6.GA</version>
+                </dependency>
+                <dependency>
+                    <groupId>org.jboss.test-harness</groupId>
+                    <artifactId>jboss-test-harness-jboss-as-51</artifactId>
+                    <version>1.0.0</version>
+                </dependency>
+            </dependencies>
+            <repositories>
+                <repository>
+                    <!-- override outdated URLs for jboss repo ids -->
+                    <id>repository.jboss.org</id>
+                    <name>JBoss Public Maven Repository Group</name>
+                    <url>https://repository.jboss.org/nexus/content/groups/public/</url>
+                    <layout>default</layout>
+                    <releases>
+                        <enabled>true</enabled>
+                        <updatePolicy>never</updatePolicy>
+                        <checksumPolicy>fail</checksumPolicy>
+                    </releases>
+                    <snapshots>
+                        <enabled>false</enabled>
+                        <updatePolicy>never</updatePolicy>
+                        <checksumPolicy>warn</checksumPolicy>
+                    </snapshots>
+                </repository>
+                <repository>
+                    <id>jboss</id>
+                    <name>JBoss Public Maven Repository Group (again)</name>
+                    <url>https://repository.jboss.org/nexus/content/groups/public/</url>
+                    <layout>default</layout>
+                    <releases>
+                        <enabled>true</enabled>
+                        <updatePolicy>never</updatePolicy>
+                        <checksumPolicy>fail</checksumPolicy>
+                    </releases>
+                    <snapshots>
+                        <enabled>false</enabled>
+                        <updatePolicy>never</updatePolicy>
+                        <checksumPolicy>warn</checksumPolicy>
+                    </snapshots>
+                </repository>
+                <repository>
+                  <id>snapshots.jboss.org</id>
+                  <url>https://repository.jboss.org/nexus/content/repositories/snapshots/</url>
+                  <snapshots>
+                    <enabled>true</enabled>
+                  </snapshots>
+                </repository>
+                <repository>
+                  <id>jboss-snapshots</id>
+                  <url>https://repository.jboss.org/nexus/content/repositories/snapshots/</url>
+                  <snapshots>
+                    <enabled>true</enabled>
+                  </snapshots>
+                </repository>
+            </repositories>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-dependency-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>copy</id>
+                                <phase>generate-test-sources</phase>
+                                <goals>
+                                    <goal>copy</goal>
+                                </goals>
+                                <configuration>
+                                    <stripVersion>true</stripVersion>
+                                    <artifactItems>
+                                        <artifactItem>
+                                            <groupId>org.hibernate.jsr303.tck</groupId>
+                                            <artifactId>jsr303-tck</artifactId>
+                                            <type>xml</type>
+                                            <classifier>suite</classifier>
+                                            <overWrite>false</overWrite>
+                                        </artifactItem>
+                                        <!-- use geronimo-validation spec
+                                        <artifactItem>
+                                            <groupId>javax.validation</groupId>
+                                            <artifactId>validation-api</artifactId>
+                                            <overWrite>true</overWrite>
+                                            <outputDirectory>${project.build.directory}/dependency/lib</outputDirectory>
+                                        </artifactItem>
+                                        -->
+                                        <artifactItem>
+                                            <groupId>org.apache.geronimo.specs</groupId>
+                                            <artifactId>geronimo-validation_1.0_spec</artifactId>
+                                            <overWrite>true</overWrite>
+                                            <outputDirectory>${project.build.directory}/dependency/lib</outputDirectory>
+                                        </artifactItem>
+                                        <artifactItem>
+                                            <groupId>org.apache.bval</groupId>
+                                            <artifactId>org.apache.bval.bundle</artifactId>
+                                            <version>${project.version}</version>
+                                            <overWrite>true</overWrite>
+                                            <outputDirectory>${project.build.directory}/dependency/lib</outputDirectory>
+                                        </artifactItem>
+                                        <artifactItem>
+                                            <groupId>org.apache.commons</groupId>
+                                            <artifactId>commons-lang3</artifactId>
+                                            <overWrite>true</overWrite>
+                                            <outputDirectory>${project.build.directory}/dependency/lib</outputDirectory>
+                                        </artifactItem>
+                                        <artifactItem>
+                                            <groupId>commons-beanutils</groupId>
+                                            <artifactId>commons-beanutils-core</artifactId>
+                                            <overWrite>true</overWrite>
+                                            <outputDirectory>${project.build.directory}/dependency/lib</outputDirectory>
+                                        </artifactItem>
+                                        <artifactItem>
+                                            <groupId>com.thoughtworks.xstream</groupId>
+                                            <artifactId>xstream</artifactId>
+                                            <overWrite>true</overWrite>
+                                            <outputDirectory>${project.build.directory}/dependency/lib</outputDirectory>
+                                        </artifactItem>
+                                    </artifactItems>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <suiteXmlFiles>
+                                <suiteXmlFile>${project.build.directory}/dependency/jsr303-tck-suite.xml</suiteXmlFile>
+                            </suiteXmlFiles>
+                            <argLine>-Xmx512m</argLine>
+                            <forkMode>once</forkMode>
+                            <systemProperties>
+                                <property>
+                                    <name>validation.provider</name>
+                                    <value>${validation.provider}</value>
+                                </property>
+                            </systemProperties>
+                        </configuration>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-report-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>generate-test-report</id>
+                                <phase>test</phase>
+                                <goals>
+                                    <goal>report-only</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                        <configuration>
+                            <outputDirectory>${project.build.directory}/surefire-reports</outputDirectory>
+                            <outputName>test-report</outputName>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <profile>
+            <id>incontainer-debug</id>
+            <activation>
+                <property>
+                    <name>debug</name>
+                </property>
+            </activation>
+            <properties>
+                <remote.debug>-Xnoagent -Djava.compiler=NONE -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005
+                </remote.debug>
+            </properties>
+        </profile>
+
+        <profile>
+            <id>incontainer</id>
+            <activation>
+                <property>
+                    <name>incontainer</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <suiteXmlFiles>
+                                <suiteXmlFile>${project.build.directory}/dependency/jsr303-tck-suite.xml</suiteXmlFile>
+                            </suiteXmlFiles>
+                            <systemProperties>
+                                <property>
+                                    <name>validation.provider</name>
+                                    <value>${validation.provider}</value>
+                                </property>
+                                <property>
+                                    <name>org.jboss.testharness.standalone</name>
+                                    <value>false</value>
+                                </property>
+                                <property>
+                                    <name>jboss.home</name>
+                                    <value>${jboss.home}</value>
+                                </property>
+                                <property>
+                                    <name>org.jboss.testharness.container.javaOpts</name>
+                                    <value>-Xms128m -Xmx512m -XX:MaxPermSize=256m -Dorg.jboss.resolver.warning=true
+                                        -Dvalidation.provider=${validation.provider} ${remote.debug}
+                                    </value>
+                                </property>
+                                <property>
+                                    <name>org.jboss.testharness.container.forceRestart</name>
+                                    <value>true</value>
+                                </property>
+                                <property>
+                                    <name>org.jboss.testharness.runIntegrationTests</name>
+                                    <value>true</value>
+                                </property>
+                                <property>
+                                    <name>org.jboss.testharness.libraryDirectory</name>
+                                    <value>target/dependency/lib</value>
+                                </property>
+                                <property>
+                                    <name>org.jboss.testharness.outputDirectory</name>
+                                    <value>target</value>
+                                </property>
+                            </systemProperties>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+
+        <profile>
+            <id>write-artifacts-to-disk</id>
+            <activation>
+                <property>
+                    <name>dumpArtifacts</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>exec-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>generate-jsr-303-artifacts</id>
+                                <phase>test-compile</phase>
+                                <goals>
+                                    <goal>java</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                        <configuration>
+                            <classpathScope>test</classpathScope>
+                            <mainClass>org.jboss.testharness.api.TCK</mainClass>
+                            <systemProperties>
+                                <systemProperty>
+                                    <key>dumpArtifacts</key>
+                                    <value>true</value>
+                                </systemProperty>
+                                <systemProperty>
+                                    <key>org.jboss.testharness.outputDirectory</key>
+                                    <value>target/jsr303-artifacts</value>
+                                </systemProperty>
+                                <systemProperty>
+                                    <key>org.jboss.testharness.libraryDirectory</key>
+                                    <value>target/jsr303-artifacts/dependencies</value>
+                                </systemProperty>
+                            </systemProperties>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
+
diff --git a/trunk/bval-tck/src/main/resources/META-INF/jboss-test-harness.properties b/trunk/bval-tck/src/main/resources/META-INF/jboss-test-harness.properties
new file mode 100644
index 0000000..895a5db
--- /dev/null
+++ b/trunk/bval-tck/src/main/resources/META-INF/jboss-test-harness.properties
@@ -0,0 +1,19 @@
+# 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.
+org.jboss.testharness.spi.StandaloneContainers=org.hibernate.jsr303.tck.util.StandaloneContainersImpl
+org.jboss.testharness.api.TestLauncher=org.jboss.testharness.impl.runner.servlet.ServletTestLauncher
+org.jboss.testharness.testPackage=org.hibernate.jsr303.tck.tests
\ No newline at end of file
diff --git a/trunk/bval-tck/src/test/resources/jndi.properties b/trunk/bval-tck/src/test/resources/jndi.properties
new file mode 100644
index 0000000..433246b
--- /dev/null
+++ b/trunk/bval-tck/src/test/resources/jndi.properties
@@ -0,0 +1,20 @@
+# 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.
+#jboss JNDI properties
+java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
+java.naming.provider.url=jnp://localhost:1099
+java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
\ No newline at end of file
diff --git a/trunk/bval-tck/src/test/resources/log4j.xml b/trunk/bval-tck/src/test/resources/log4j.xml
new file mode 100644
index 0000000..0ce01ab
--- /dev/null
+++ b/trunk/bval-tck/src/test/resources/log4j.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<!--
+ 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.
+-->
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+
+    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+        <param name="Target" value="System.out"/>
+        <layout class="org.apache.log4j.PatternLayout">
+            <!-- The default pattern: Date Priority [Category] Message\n -->
+            <param name="ConversionPattern" value="%d{ABSOLUTE} %-5p [%c{2}] %m%n"/>
+        </layout>
+        <filter class="org.apache.log4j.varia.StringMatchFilter">
+            <param name="AcceptOnMatch" value="false"/>
+            <param name="StringToMatch" value="Failure while notifying an observer of event [a]"/>
+        </filter>
+        <filter class="org.apache.log4j.varia.StringMatchFilter">
+            <param name="AcceptOnMatch" value="false"/>
+            <param name="StringToMatch"
+                   value="Error destroying Request scoped unnamed simple bean org.jboss.jsr299.tck.tests.implementation.simple.lifecycle.Cod"/>
+        </filter>
+    </appender>
+
+    <!-- ############### Categories #################  -->
+    <category name="org.apache">
+        <priority value="INFO"/>
+    </category>
+
+    <category name="org.hibernate">
+        <priority value="INFO"/>
+    </category>
+
+    <category name="org.jboss.testharness">
+        <priority value="INFO"/>
+    </category>
+
+    <root>
+        <priority value="INFO"/>
+        <appender-ref ref="CONSOLE"/>
+    </root>
+
+</log4j:configuration>
diff --git a/trunk/bval-xstream/findbugs-exclude-filter.xml b/trunk/bval-xstream/findbugs-exclude-filter.xml
new file mode 100644
index 0000000..eaf41b1
--- /dev/null
+++ b/trunk/bval-xstream/findbugs-exclude-filter.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+
+<!--
+  This file contains some false positive bugs detected by findbugs. Their
+  false positive nature has been analyzed individually and they have been
+  put here to instruct findbugs it must ignore them.
+-->
+<FindBugsFilter>
+  <!-- not a problem -->
+  <Match>
+    <Class name="org.apache.bval.xml.XMLMetaBeanManager" />
+    <Method name="computeRelatedMetaBean" params="org.apache.bval.model.MetaProperty,java.lang.String" returns="void" />
+    <Bug pattern="NP_LOAD_OF_KNOWN_NULL_VALUE" />
+  </Match>
+</FindBugsFilter>
diff --git a/trunk/bval-xstream/pom.xml b/trunk/bval-xstream/pom.xml
new file mode 100644
index 0000000..d156a5e
--- /dev/null
+++ b/trunk/bval-xstream/pom.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<!--
+	Maven release plugin requires the project tag to be on a single line.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.bval</groupId>
+    <artifactId>bval-parent</artifactId>
+    <version>0.4</version>
+  </parent>
+
+  <artifactId>bval-xstream</artifactId>
+  <name>Apache BVal :: bval-xstream (optional)</name>
+  <packaging>jar</packaging>
+
+  <description>BVal XML Metadata with XStream</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.bval</groupId>
+      <artifactId>bval-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.thoughtworks.xstream</groupId>
+      <artifactId>xstream</artifactId>
+    </dependency>
+    <!-- Test dependencies -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>jcl-over-slf4j</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <!-- create mainClass attribute -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>default-jar</id>
+            <goals>
+              <goal>jar</goal>
+            </goals>
+            <configuration>
+              <archive>
+                <manifest>
+                  <mainClass>org.apache.bval.util.BValVersion</mainClass>
+                </manifest>
+              </archive>
+            </configuration>
+          </execution>
+          <execution>
+            <id>attach-tests</id>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>findbugs-maven-plugin</artifactId>
+        <configuration>
+            <excludeFilterFile>findbugs-exclude-filter.xml</excludeFilterFile>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
+
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/routines/EMailValidation.java b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/EMailValidation.java
new file mode 100644
index 0000000..9cd04f4
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/EMailValidation.java
@@ -0,0 +1,48 @@
+/*
+ *  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.bval.routines;
+
+
+import org.apache.bval.model.Validation;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+
+import java.util.regex.Pattern;
+
+/**
+ * Description: example validation for email addresses using a regular expression<br/>
+ */
+public class EMailValidation implements Validation {
+
+    private java.util.regex.Pattern pattern = EMailValidationUtils.DEFAULT_EMAIL_PATTERN;
+
+    public <T extends ValidationListener> void validate(ValidationContext<T> context) {
+        if (context.getPropertyValue() == null) return;
+        if (!EMailValidationUtils.isValid(context.getPropertyValue(), getPattern())) {
+            context.getListener().addError(Reasons.EMAIL_ADDRESS, context);
+        }
+    }
+
+    public Pattern getPattern() {
+        return pattern;
+    }
+
+    public void setPattern(Pattern pattern) {
+        this.pattern = pattern;
+    }
+
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/routines/NOPValidation.java b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/NOPValidation.java
new file mode 100644
index 0000000..6b63e3a
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/NOPValidation.java
@@ -0,0 +1,31 @@
+/*
+ *  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.bval.routines;
+
+import org.apache.bval.model.Validation;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+
+/**
+ * Description: DO NOTHING VALIDATION (can be used to turn off standard validation)<br/>
+ */
+public class NOPValidation implements Validation {
+
+    public <T extends ValidationListener> void validate(ValidationContext<T> context) {
+        // do nothing
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/routines/Reasons.java b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/Reasons.java
new file mode 100644
index 0000000..397081f
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/Reasons.java
@@ -0,0 +1,31 @@
+/*
+ *  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.bval.routines;
+
+import org.apache.bval.model.Features;
+
+/**
+ * Description: StandardReasons for validation errors found in
+ * {@link org.apache.bval.ValidationResults}<br/>
+ */
+public interface Reasons extends Features.Property {
+    // The reasons inherited from Features are VALIDATION features only.
+    // INFO features are not meant to be validated.
+
+    // Add more reasons here.
+    String EMAIL_ADDRESS = "emailAddress";
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/routines/StandardValidation.java b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/StandardValidation.java
new file mode 100644
index 0000000..c78f3b5
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/routines/StandardValidation.java
@@ -0,0 +1,184 @@
+/*
+ *  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.bval.routines;
+
+import static org.apache.bval.model.Features.Property.MANDATORY;
+import static org.apache.bval.model.Features.Property.MAX_LENGTH;
+import static org.apache.bval.model.Features.Property.MAX_VALUE;
+import static org.apache.bval.model.Features.Property.MIN_LENGTH;
+import static org.apache.bval.model.Features.Property.MIN_VALUE;
+import static org.apache.bval.model.Features.Property.REG_EXP;
+import static org.apache.bval.model.Features.Property.TIME_LAG;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.model.Validation;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+import org.apache.bval.xml.XMLMetaValue;
+
+/**
+ * Description: This class implements the standard validations for properties!
+ * You can subclass this class and replace the implementation
+ * in the beanInfo-xml by providing it a validation "standard"<br/>
+ */
+public class StandardValidation implements Validation {
+
+    /** key for this validation in the validation list of the beanInfos */
+    public String getValidationId() {
+        return "standard";
+    }
+
+    public <T extends ValidationListener> void validate(ValidationContext<T> context) {
+        validateMandatory(context);
+        validateMaxLength(context);
+        validateMinLength(context);
+        validateMaxValue(context);
+        validateMinValue(context);
+        validateRegExp(context);
+        validateTimeLag(context);
+    }
+
+    protected <T extends ValidationListener> void validateTimeLag(ValidationContext<T> context) {
+        String lag = (String) context.getMetaProperty().getFeature(TIME_LAG);
+        if (lag == null) return;
+        if (context.getPropertyValue() == null) return;
+        long date = ((Date) context.getPropertyValue()).getTime();
+        long now = System.currentTimeMillis();
+        if (XMLMetaValue.TIMELAG_Future.equals(lag)) {
+            if (date < now) {
+                context.getListener().addError(TIME_LAG, context);
+            }
+        } else if (XMLMetaValue.TIMELAG_Past.equals(lag)) {
+            if (date > now) {
+                context.getListener().addError(TIME_LAG, context);
+            }
+        } else {
+            throw new IllegalArgumentException("unknown timelag " + lag + " at " + context);
+        }
+    }
+
+    private static final String REG_EXP_PATTERN = "cachedRegExpPattern";
+
+    protected <T extends ValidationListener> void validateRegExp(ValidationContext<T> context) {
+        final MetaProperty meta = context.getMetaProperty();
+        final String regExp = (String) meta.getFeature(REG_EXP);
+        if (regExp == null) return;
+        if (context.getPropertyValue() == null) return;
+
+        final String value = String.valueOf(context.getPropertyValue());
+        try {
+            Pattern pattern = (Pattern) meta.getFeature(REG_EXP_PATTERN);
+            if (pattern == null) {
+                pattern = Pattern.compile(regExp);
+                meta.putFeature(REG_EXP_PATTERN, pattern);
+            }
+            if (!pattern.matcher(value).matches()) {
+                context.getListener().addError(REG_EXP, context);
+            }
+        } catch (PatternSyntaxException e) {
+            throw new IllegalArgumentException(
+                  "regular expression malformed. regexp " + regExp + " at " + context, e);
+        }
+    }
+
+    protected <T extends ValidationListener> void validateMinValue(ValidationContext<T> context) {
+        @SuppressWarnings("unchecked")
+        Comparable<Object> minValue = (Comparable<Object>) context.getMetaProperty().getFeature(MIN_VALUE);
+        if (minValue == null || context.getPropertyValue() == null) return;
+        if (compare(context, minValue, context.getPropertyValue()) > 0) {
+            context.getListener().addError(MIN_VALUE, context);
+        }
+    }
+
+    protected <T extends ValidationListener> void validateMaxValue(ValidationContext<T> context) {
+        @SuppressWarnings("unchecked")
+        Comparable<Object> maxValue = (Comparable<Object>) context.getMetaProperty().getFeature(MAX_VALUE);
+        if (maxValue == null || context.getPropertyValue() == null) return;
+        if (compare(context, maxValue, context.getPropertyValue()) < 0) {
+            context.getListener().addError(MAX_VALUE, context);
+        }
+    }
+
+    private <T extends ValidationListener> int compare(ValidationContext<T> context, Comparable<Object> constraintValue,
+                        Object currentValue) {
+        int r;
+        if (constraintValue.getClass().isAssignableFrom(currentValue.getClass())) {
+            r = constraintValue.compareTo(context.getPropertyValue());
+        } else if (currentValue instanceof Number) {
+            double dv = ((Number) currentValue).doubleValue();
+            double mdv = ((Number) constraintValue).doubleValue();
+            r = mdv > dv ? 1 : -1;
+        } else {
+            r = String.valueOf(constraintValue).compareTo(String.valueOf(currentValue));
+        }
+        return r;
+    }
+
+    protected <T extends ValidationListener> void validateMaxLength(ValidationContext<T> context) {
+        Integer maxLength = (Integer) context.getMetaProperty()
+              .getFeature(Features.Property.MAX_LENGTH);
+        if (maxLength == null) return;
+        if (context.getPropertyValue() == null) return;
+
+        final Object value = context.getPropertyValue();
+        int length = 0;
+        if (value instanceof String) {
+            length = ((String) value).length();
+        } else if (value instanceof Collection<?>) {
+            length = ((Collection<?>) value).size();
+        }
+        if (length > maxLength) {
+            context.getListener().addError(MAX_LENGTH, context);
+        }
+    }
+
+    protected <T extends ValidationListener> void validateMinLength(ValidationContext<T> context) {
+        Integer maxLength = (Integer) context.getMetaProperty()
+              .getFeature(Features.Property.MIN_LENGTH);
+        if (maxLength == null) return;
+        if (context.getPropertyValue() == null) return;
+
+        final Object value = context.getPropertyValue();
+        int length = 0;
+        if (value instanceof String) {
+            length = ((String) value).length();
+        } else if (value instanceof Collection<?>) {
+            length = ((Collection<?>) value).size();
+        }
+        if (length < maxLength) {
+            context.getListener().addError(MIN_LENGTH, context);
+        }
+    }
+
+    protected <T extends ValidationListener> void validateMandatory(ValidationContext<T> context) {
+        if (context.getMetaProperty().isMandatory()) {
+            if (context.getPropertyValue() == null) {
+                context.getListener().addError(MANDATORY, context);
+            }
+        }
+    }
+
+    public static StandardValidation getInstance() {
+        return new StandardValidation();
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/MetaBeanEnricher.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/MetaBeanEnricher.java
new file mode 100644
index 0000000..e7438b3
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/MetaBeanEnricher.java
@@ -0,0 +1,38 @@
+/*
+ *  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.bval.xml;
+
+
+import org.apache.bval.model.MetaBean;
+
+import java.util.Map;
+
+/**
+ * Description: Interface to merge meta beans<br/>
+ */
+public interface MetaBeanEnricher {
+
+    /**
+     * @param infos - the patches to apply
+     * @return all MetaBeans for classes that have a xml descriptor and
+     *         additional the MetaBeans loaded by the given loaders.
+     *         The given loaders may also return patches for MetaBeans that have
+     *         also been returned by other loaders. The beans with patches for
+     *         references to patched beans will be copied.
+     */
+    Map<String, MetaBean> enrichCopies(XMLMetaBeanInfos... infos);
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLFeaturesCapable.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLFeaturesCapable.java
new file mode 100644
index 0000000..3e9bf88
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLFeaturesCapable.java
@@ -0,0 +1,98 @@
+/*
+ *  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.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamImplicit;
+import org.apache.bval.model.FeaturesCapable;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Description: <br/>
+ */
+public class XMLFeaturesCapable implements Serializable {
+    /** Serialization version */
+    private static final long serialVersionUID = 1L;
+
+    @XStreamImplicit
+    private List<XMLMetaFeature> features;
+    @XStreamImplicit(itemFieldName = "validator")
+    private List<XMLMetaValidatorReference> validators;
+
+    public List<XMLMetaFeature> getFeatures() {
+        return features;
+    }
+
+    public void setFeatures(List<XMLMetaFeature> features) {
+        this.features = features;
+    }
+
+    public void putFeature(String key, Object value) {
+        XMLMetaFeature anno = findFeature(key);
+        if (features == null) features = new ArrayList<XMLMetaFeature>();
+        if (anno == null) {
+            features.add(new XMLMetaFeature(key, value));
+        } else {
+            anno.setValue(value);
+        }
+    }
+
+    public void removeFeature(String key) {
+        XMLMetaFeature anno = findFeature(key);
+        if (anno != null) {
+            getFeatures().remove(anno);
+        }
+    }
+
+    public Object getFeature(String key) {
+        XMLMetaFeature anno = findFeature(key);
+        return anno == null ? null : anno.getValue();
+    }
+
+    private XMLMetaFeature findFeature(String key) {
+        if (features == null) return null;
+        for (XMLMetaFeature anno : features) {
+            if (key.equals(anno.getKey())) return anno;
+        }
+        return null;
+    }
+
+    public List<XMLMetaValidatorReference> getValidators() {
+        return validators;
+    }
+
+    public void setValidators(List<XMLMetaValidatorReference> validators) {
+        this.validators = validators;
+    }
+
+    public void addValidator(String validatorId) {
+        if (validators == null) validators = new ArrayList<XMLMetaValidatorReference>();
+        validators.add(new XMLMetaValidatorReference(validatorId));
+    }
+
+    public void mergeFeaturesInto(FeaturesCapable fc) {
+        if (getFeatures() != null) {
+            fc.optimizeRead(false);
+            for (XMLMetaFeature each : getFeatures()) {
+                fc.putFeature(each.getKey(), each.getValue());
+            }
+            fc.optimizeRead(true);
+        }
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMapper.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMapper.java
new file mode 100644
index 0000000..72284b7
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMapper.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.bval.xml;
+
+import com.thoughtworks.xstream.XStream;
+
+/**
+ * Description: <br/>
+ */
+public class XMLMapper {
+    private static final XMLMapper instance = new XMLMapper();
+
+    private final XStream xStream;
+
+    private XMLMapper() {
+        xStream = new XStream();
+        xStream.processAnnotations(new Class[] {XMLFeaturesCapable.class,
+                XMLMetaFeature.class, XMLMetaBean.class, XMLMetaBeanInfos.class,
+                XMLMetaBeanReference.class, XMLMetaElement.class, XMLMetaProperty.class,
+                XMLMetaValidator.class, XMLMetaValidatorReference.class});
+        xStream.setMode(XStream.NO_REFERENCES);
+    }
+
+    public static XMLMapper getInstance() {
+        return instance;
+    }
+
+    public XStream getXStream() {
+        return xStream;
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBean.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBean.java
new file mode 100644
index 0000000..9f516df
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBean.java
@@ -0,0 +1,156 @@
+/*
+ *  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.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import com.thoughtworks.xstream.annotations.XStreamImplicit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Description: <br/>
+ */
+@XStreamAlias("bean")
+public class XMLMetaBean extends XMLFeaturesCapable {
+    /** Serialization version */
+    private static final long serialVersionUID = 1L;
+
+    @XStreamAsAttribute()
+    private String id;
+    @XStreamAsAttribute()
+    private String name;
+    @XStreamAsAttribute()
+    private String impl;
+    @XStreamImplicit
+    private List<XMLMetaProperty> properties;
+    @XStreamImplicit
+    private List<XMLMetaBeanReference> beanRelations;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getImpl() {
+        return impl;
+    }
+
+    public void setImpl(String impl) {
+        this.impl = impl;
+    }
+
+    public List<XMLMetaProperty> getProperties() {
+        return properties;
+    }
+
+    public void setProperties(List<XMLMetaProperty> properties) {
+        this.properties = properties;
+    }
+
+    public void addProperty(XMLMetaProperty property) {
+        if (properties == null) properties = new ArrayList<XMLMetaProperty>();
+        properties.add(property);
+    }
+
+    public void putProperty(XMLMetaProperty property) {
+        if (property.getName() != null) {
+            XMLMetaProperty prop = findProperty(property.getName());
+            if (prop != null) {
+                properties.remove(prop);
+            }
+        }
+        addProperty(property);
+    }
+
+    public XMLMetaProperty removeProperty(String name) {
+        XMLMetaProperty prop = findProperty(name);
+        if (prop != null) {
+            properties.remove(prop);
+        }
+        return prop;
+    }
+
+    public XMLMetaProperty getProperty(String name) {
+        return findProperty(name);
+    }
+
+    private XMLMetaProperty findProperty(String name) {
+        if (properties == null) return null;
+        for (XMLMetaProperty prop : properties) {
+            if (name.equals(prop.getName())) return prop;
+        }
+        return null;
+    }
+
+    public List<XMLMetaBeanReference> getBeanRefs() {
+        return beanRelations;
+    }
+
+    public void setBeanRefs(List<XMLMetaBeanReference> beanRelations) {
+        this.beanRelations = beanRelations;
+    }
+
+
+    public void addBeanRef(XMLMetaBeanReference beanRelation) {
+        if (beanRelations == null) beanRelations = new ArrayList<XMLMetaBeanReference>();
+        beanRelations.add(beanRelation);
+    }
+
+    public void putBeanRef(XMLMetaBeanReference beanRelation) {
+        if (beanRelation.getName() != null) {
+            XMLMetaBeanReference relation = findBeanRef(beanRelation.getName());
+            if (relation != null) {
+                beanRelations.remove(relation);
+            }
+        }
+        addBeanRef(beanRelation);
+    }
+
+    public XMLMetaBeanReference removeBeanRef(String name) {
+        XMLMetaBeanReference relation = findBeanRef(name);
+        if (relation != null) {
+            beanRelations.remove(relation);
+        }
+        return relation;
+    }
+
+    public XMLMetaBeanReference getBeanRef(String name) {
+        return findBeanRef(name);
+    }
+
+    private XMLMetaBeanReference findBeanRef(String name) {
+        if (beanRelations == null) return null;
+        for (XMLMetaBeanReference relation : beanRelations) {
+            if (name.equals(relation.getName())) return relation;
+        }
+        return null;
+    }
+
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanBuilder.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanBuilder.java
new file mode 100644
index 0000000..8229183
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanBuilder.java
@@ -0,0 +1,178 @@
+/*
+ *  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.bval.xml;
+
+import org.apache.bval.IntrospectorMetaBeanFactory;
+import org.apache.bval.MetaBeanBuilder;
+import org.apache.bval.MetaBeanFactory;
+import org.apache.bval.model.MetaBean;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Description: <br>
+ * User: roman.stumm<br>
+ * Date: 17.06.2010<br>
+ * Time: 10:10:46<br>
+ */
+public class XMLMetaBeanBuilder extends MetaBeanBuilder {
+    private XMLMetaBeanFactory xmlFactory;
+
+    public XMLMetaBeanBuilder(MetaBeanFactory[] factories) {
+        setFactories(factories);
+    }
+
+    public XMLMetaBeanBuilder() {
+        setFactories(new MetaBeanFactory[] { new IntrospectorMetaBeanFactory(), new XMLMetaBeanFactory() });
+    }
+
+    public void setFactories(MetaBeanFactory[] factories) {
+        super.setFactories(factories);
+        updateXmlFactory();
+    }
+
+    public void addLoader(XMLMetaBeanLoader loader) {
+        assertXmlFactory();
+        xmlFactory.addLoader(loader);
+    }
+
+    public MetaBean buildForId(String beanInfoId) throws Exception {
+        final XMLMetaBeanFactory.Visitor v;
+        assertXmlFactory();
+        xmlFactory.visitXMLBeanMeta(beanInfoId, v = new XMLMetaBeanFactory.Visitor() {
+            private MetaBean meta;
+
+            public MetaBean getMetaBean() {
+                return meta;
+            }
+
+            public void visit(XMLMetaBean xmlMeta, XMLMetaBeanInfos xmlInfos) throws Exception {
+                if (meta == null) {
+                    meta = createMetaBean(xmlMeta);
+                }
+                xmlFactory.enrichMetaBean(meta, new XMLMetaBeanFactory.XMLResult(xmlMeta, xmlInfos));
+            }
+
+        });
+        if (v.getMetaBean() == null) {
+            throw new IllegalArgumentException("MetaBean " + beanInfoId + " not found");
+        }
+        return v.getMetaBean();
+    }
+
+    public Map<String, MetaBean> buildAll() throws Exception {
+        final Map<String, MetaBean> all = super.buildAll();
+        if (xmlFactory != null) {
+            xmlFactory.visitXMLBeanMeta(null, new XMLMetaBeanFactory.Visitor() {
+                public void visit(XMLMetaBean empty, XMLMetaBeanInfos xmlInfos) throws Exception {
+                    if (xmlInfos.getBeans() == null)
+                        return; // empty file, ignore
+                    XMLMetaBeanFactory.XMLResult carrier = new XMLMetaBeanFactory.XMLResult(null, xmlInfos);
+
+                    for (XMLMetaBean xmlMeta : xmlInfos.getBeans()) {
+                        MetaBean meta = all.get(xmlMeta.getId());
+                        if (meta == null) {
+                            meta = createMetaBean(xmlMeta);
+                            all.put(xmlMeta.getId(), meta);
+                        }
+                        carrier.xmlMeta = xmlMeta;
+                        xmlFactory.enrichMetaBean(meta, carrier);
+                    }
+                }
+
+                public MetaBean getMetaBean() {
+                    return null; // do nothing
+                }
+            });
+        }
+        return all;
+    }
+
+    public Map<String, MetaBean> enrichCopies(Map<String, MetaBean> all, XMLMetaBeanInfos... infosArray)
+        throws Exception {
+        assertXmlFactory();
+        final Map<String, MetaBean> copies = new HashMap<String, MetaBean>(all.size());
+        boolean nothing = true;
+        XMLMetaBeanFactory.XMLResult carrier = new XMLMetaBeanFactory.XMLResult();
+        for (XMLMetaBeanInfos xmlMetaBeanInfos : infosArray) {
+            carrier.xmlInfos = xmlMetaBeanInfos;
+            if (xmlMetaBeanInfos == null)
+                continue;
+            try {
+                for (XMLMetaBean xmlMeta : xmlMetaBeanInfos.getBeans()) {
+                    nothing = false;
+                    MetaBean copy = copies.get(xmlMeta.getId());
+                    if (copy == null) { // ist noch nicht kopiert
+                        MetaBean meta = all.get(xmlMeta.getId());
+                        if (meta == null) { // gibt es nicht
+                            copy = createMetaBean(xmlMeta);
+                        } else { // gibt es, jetzt kopieren
+                            copy = meta.copy();
+                        }
+                        copies.put(xmlMeta.getId(), copy);
+                    }
+                    carrier.xmlMeta = xmlMeta;
+                    xmlFactory.enrichMetaBean(copy, carrier);
+                }
+            } catch (IOException e) {
+                xmlFactory.handleLoadException(xmlMetaBeanInfos, e);
+            }
+        }
+        if (nothing)
+            return all;
+        for (Map.Entry<String, MetaBean> entry : all.entrySet()) {
+            /*
+             * alle unveraenderten werden AUCH KOPIERT (nur zwar nur, wegen
+             * potentieller CrossReferenzen durch Relationships)
+             */
+            if (!copies.containsKey(entry.getKey())) {
+                if (entry.getValue().hasRelationships()) {
+                    copies.put(entry.getKey(), (MetaBean) entry.getValue().copy());
+                } else { // no relationship: do not clone()
+                    copies.put(entry.getKey(), entry.getValue());
+                }
+            }
+        }
+        return copies;
+    }
+
+    private MetaBean createMetaBean(XMLMetaBean xmlMeta) throws Exception {
+        return buildForClass(findLocalClass(xmlMeta.getImpl()));
+    }
+
+    private void updateXmlFactory() {
+        for (MetaBeanFactory each : getFactories()) {
+            if (each instanceof XMLMetaBeanFactory) { // use the first one!
+                xmlFactory = (XMLMetaBeanFactory) each;
+                return;
+            }
+        }
+        xmlFactory = null; // none
+    }
+
+    public XMLMetaBeanFactory getXmlFactory() {
+        return xmlFactory;
+    }
+
+    private void assertXmlFactory() {
+        if (xmlFactory == null) {
+            throw new IllegalStateException("no xmlFactory available");
+        }
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanFactory.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanFactory.java
new file mode 100644
index 0000000..83454ef
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanFactory.java
@@ -0,0 +1,241 @@
+/*
+ *  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.bval.xml;
+
+import static org.apache.bval.model.Features.Property.JAVASCRIPT_VALIDATION_FUNCTIONS;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.bval.MetaBeanFactory;
+import org.apache.bval.model.FeaturesCapable;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.routines.StandardValidation;
+
+/**
+ * Description: Create or enrich MetaBeans from apache beanInfos xml<br/>
+ */
+public class XMLMetaBeanFactory implements MetaBeanFactory {
+    private static final Logger logger = Logger.getLogger(XMLMetaBeanFactory.class.getName());
+
+    // use LinkedHashMap to keep sequence of loaders
+    private final Map<XMLMetaBeanLoader, XMLMetaBeanInfos> resources =
+          new LinkedHashMap<XMLMetaBeanLoader, XMLMetaBeanInfos>();
+
+    private StandardValidation standardValidation = StandardValidation.getInstance();
+
+    public interface Visitor {
+        /**
+         * @param xmlMeta  - null or the bean found
+         * @param xmlInfos - all infos in a single unit (xml file)
+         * @throws Exception
+         */
+        void visit(XMLMetaBean xmlMeta, XMLMetaBeanInfos xmlInfos) throws Exception;
+
+        MetaBean getMetaBean();
+    }
+
+    public static class XMLResult {
+        public XMLMetaBean xmlMeta;
+        public XMLMetaBeanInfos xmlInfos;
+
+        public XMLResult(XMLMetaBean metaBean, XMLMetaBeanInfos metaInfos) {
+            this.xmlMeta = metaBean;
+            this.xmlInfos = metaInfos;
+        }
+
+        public XMLResult() {
+        }
+    }
+
+    public void buildMetaBean(final MetaBean metaBean) throws Exception {
+        if(metaBean.getId() == null) return;
+         visitXMLBeanMeta(metaBean.getId(), new Visitor() {
+            public void visit(XMLMetaBean xmlMeta, XMLMetaBeanInfos xmlInfos)
+                  throws Exception {
+                enrichMetaBean(metaBean, new XMLResult(xmlMeta, xmlInfos));
+            }
+
+            public MetaBean getMetaBean() {
+                return metaBean;
+            }
+        });
+    }
+
+    /** XMLMetaBeanLoader are used to know "locations" where to get BeanInfos from. */
+    public Collection<XMLMetaBeanLoader> getLoaders() {
+        return resources.keySet();
+    }
+
+    public void addLoader(XMLMetaBeanLoader loader) {
+        resources.put(loader, null);
+    }
+
+    public StandardValidation getStandardValidation() {
+        return standardValidation;
+    }
+
+    /** customize the implementation of standardValidation for this builder. */
+    public void setStandardValidation(StandardValidation standardValidation) {
+        this.standardValidation = standardValidation;
+    }
+
+    public void enrichMetaBean(MetaBean meta, XMLResult result) throws Exception {
+        if (result.xmlMeta.getId() != null) {
+            meta.setId(result.xmlMeta.getId());
+        }
+        if (result.xmlMeta.getName() != null) {
+            meta.setName(result.xmlMeta.getName());
+        }
+/*        if (meta.getBeanClass() == null && result.xmlMeta.getImpl() != null) {
+            meta.setBeanClass(findLocalClass(result.xmlMeta.getImpl()));
+        }*/
+        result.xmlMeta.mergeFeaturesInto(meta);
+        enrichValidations(meta, result.xmlMeta, result, false);
+        if (result.xmlMeta.getProperties() != null) {
+            for (XMLMetaProperty xmlProp : result.xmlMeta.getProperties()) {
+                enrichElement(meta, xmlProp, result);
+            }
+        }
+        if (result.xmlMeta.getBeanRefs() != null) {
+            for (XMLMetaBeanReference xmlRef : result.xmlMeta.getBeanRefs()) {
+                enrichElement(meta, xmlRef, result);
+            }
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    protected void enrichValidations(FeaturesCapable prop, XMLFeaturesCapable xmlProp,
+                                     XMLResult result, boolean addStandard)
+          throws Exception {
+        if (xmlProp.getValidators() != null) {
+            // obsolete code? remove from here --->
+            String[] func = prop.getFeature(JAVASCRIPT_VALIDATION_FUNCTIONS);
+            List<String> jsValidators = new ArrayList<String>(
+                  xmlProp.getValidators().size() + (func == null ? 0 : func.length));
+            if (func != null && func.length > 0) {
+                jsValidators.addAll(Arrays.asList(func));
+            }  // <--- to here
+            boolean useStandard = prop instanceof MetaProperty;
+            for (XMLMetaValidatorReference valRef : xmlProp.getValidators()) {
+                if (standardValidation != null &&
+                      valRef.getRefId().equals(standardValidation.getValidationId())) {
+                    useStandard = false;
+                }
+                XMLMetaValidator validator =
+                      result.xmlInfos.getValidator(valRef.getRefId());
+                if (validator != null) {
+                    if (validator.getValidation() != null) {
+                        prop.addValidation(validator.getValidation());
+                    }
+                    if (validator.getJsFunction() != null &&
+                          !jsValidators.contains(validator.getJsFunction())) {
+                        jsValidators.add(validator.getJsFunction());
+                    }
+                }
+            }
+            if (!jsValidators.isEmpty()) {
+                prop.putFeature(JAVASCRIPT_VALIDATION_FUNCTIONS,
+                      jsValidators.toArray(new String[jsValidators.size()]));
+            }
+            if (useStandard && standardValidation != null) {
+                if (!prop.hasValidation(standardValidation))
+                    prop.addValidation(standardValidation);
+            }
+        } else if (addStandard && standardValidation != null &&
+              !prop.hasValidation(standardValidation)) {
+            prop.addValidation(standardValidation);
+        }
+    }
+
+    protected MetaProperty enrichElement(MetaBean meta, XMLMetaElement xmlProp,
+                                         XMLResult result) throws Exception {
+        MetaProperty prop = meta.getProperty(xmlProp.getName());
+        if (prop == null) {
+            prop = new MetaProperty();
+            prop.setName(xmlProp.getName());
+            meta.putProperty(xmlProp.getName(), prop);
+        }
+        xmlProp.mergeInto(prop);
+        enrichValidations(prop, xmlProp, result, true);
+        return prop;
+    }
+
+
+    public void visitXMLBeanMeta(String beanId, Visitor visitor) throws Exception {
+        for (Map.Entry<XMLMetaBeanLoader, XMLMetaBeanInfos> entry : resources
+              .entrySet()) {
+            if (entry.getValue() == null) {
+                // load when not already loaded
+                try {
+                    entry.setValue(entry.getKey().load());
+                } catch (IOException e) {
+                    handleLoadException(entry.getKey(), e);
+                }
+            }
+            if (entry.getValue() != null) { // search in loaded infos for the 'name'
+                if (beanId == null) {
+                    visitor.visit(null, entry.getValue());
+                } else {
+                    XMLMetaBean found = entry.getValue().getBean(beanId);
+                    if (found != null) {
+                        visitor.visit(found, entry.getValue());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * find a bean by the bean-id (=bean.name)
+     *
+     * @return null or the bean found from the first loader that has it.
+     */
+    protected XMLResult findXMLBeanMeta(String beanId) {
+        for (Map.Entry<XMLMetaBeanLoader, XMLMetaBeanInfos> entry : resources
+              .entrySet()) {
+            if (entry.getValue() == null) {
+                // load when not already loaded
+                try {
+                    entry.setValue(entry.getKey().load());
+                } catch (IOException e) {
+                    handleLoadException(entry.getKey(), e);
+                }
+            }
+            if (entry.getValue() != null) { // search in loaded infos for the 'name'
+                XMLMetaBean found = entry.getValue().getBean(beanId);
+                if (found != null) {
+                    return new XMLResult(found, entry.getValue());
+                }
+            }
+        }
+        return null; // not found!
+    }
+
+    public void handleLoadException(Object loader, IOException e) {
+        logger.log(Level.SEVERE, String.format("Error loading %s", loader), e);
+    }
+
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanInfos.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanInfos.java
new file mode 100644
index 0000000..cd2ac88
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanInfos.java
@@ -0,0 +1,134 @@
+/*
+ *  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.bval.xml;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.bval.model.Validation;
+import org.apache.commons.lang3.ClassUtils;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import com.thoughtworks.xstream.annotations.XStreamImplicit;
+import com.thoughtworks.xstream.annotations.XStreamOmitField;
+
+/**
+ * Description: root element of a xml-beanInfos document<br/>
+ */
+@XStreamAlias("beanInfos")
+public class XMLMetaBeanInfos {
+    @XStreamAsAttribute
+    private String id;
+    @XStreamAsAttribute
+    private String version;
+    @XStreamImplicit
+    private List<XMLMetaValidator> validators;
+    @XStreamImplicit
+    private List<XMLMetaBean> beans;
+    @XStreamOmitField
+    private Map<String, XMLMetaBean> beanLookup;
+    @XStreamOmitField
+    private Map<String, XMLMetaValidator> validationLookup;
+
+    /**
+     * used for identification, may be empty, if there is no database origin for this object.
+     * could also contain a file-name - can be used flexible...
+     */
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    /**
+     * used for change-detection, when some other component caches MetaBeans based on this
+     * object. when the version changes, the cache could compare to its version state and recompute.
+     * can be used flexible...
+     */
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public List<XMLMetaValidator> getValidators() {
+        return validators;
+    }
+
+    public void setValidators(List<XMLMetaValidator> validators) {
+        this.validators = validators;
+    }
+
+    public List<XMLMetaBean> getBeans() {
+        return beans;
+    }
+
+    public void setBeans(List<XMLMetaBean> beans) {
+        this.beans = beans;
+    }
+
+    public XMLMetaBean getBean(String id) {
+        if (beans == null) return null;
+        if (beanLookup == null) initBeanLookup();
+        return beanLookup.get(id);
+    }
+
+    private void initBeanLookup() {
+        final HashMap<String, XMLMetaBean> map = new HashMap<String, XMLMetaBean>(beans.size());
+        for (XMLMetaBean bean : beans) {
+            map.put(bean.getId(), bean);
+        }
+        beanLookup = new ConcurrentHashMap<String, XMLMetaBean>(map);
+    }
+
+    private void initValidationLookup() throws Exception {
+        final HashMap<String, XMLMetaValidator> map = new HashMap<String, XMLMetaValidator>(validators.size());
+        for (XMLMetaValidator xv : validators) {
+            if (xv.getJava() != null) {
+                Validation validation =
+                        (Validation) ClassUtils.getClass(xv.getJava()).newInstance();
+                xv.setValidation(validation);
+                map.put(xv.getId(), xv);
+            }
+        }
+        validationLookup = new ConcurrentHashMap<String, XMLMetaValidator>(map);
+    }
+
+    public void addBean(XMLMetaBean bean) {
+        if (beans == null) beans = new ArrayList<XMLMetaBean>();
+        beans.add(bean);
+    }
+
+    public XMLMetaValidator getValidator(String id) throws Exception {
+        if (validators == null) return null;
+        if (validationLookup == null) initValidationLookup();
+        return validationLookup.get(id);
+    }
+
+    public void addValidator(XMLMetaValidator validator) {
+        if (validators == null) validators = new ArrayList<XMLMetaValidator>();
+        validators.add(validator);
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanLoader.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanLoader.java
new file mode 100644
index 0000000..851c74e
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanLoader.java
@@ -0,0 +1,26 @@
+/*
+ *  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.bval.xml;
+
+import java.io.IOException;
+
+/**
+ * Description: XMLMetaBeanLoader are used to know "locations" where to get BeanInfos from.<br/>
+ */
+public interface XMLMetaBeanLoader {
+    XMLMetaBeanInfos load() throws IOException;
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanManager.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanManager.java
new file mode 100644
index 0000000..0ab8d52
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanManager.java
@@ -0,0 +1,131 @@
+/*
+ *  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.bval.xml;
+
+import org.apache.bval.MetaBeanManager;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.util.PrivilegedActions;
+
+import java.util.Map;
+
+import static org.apache.bval.model.Features.Property.REF_BEAN_ID;
+
+/**
+ * Description: internal implementation class to construct metabeans with
+ * factories and from xstream xml files. You can register different
+ * XMLMetaBeanLoaders (see addLoader()) to register xstream-xml-files that
+ * contain meta-data. You can merge + unify meta data with method
+ * enrichCopies(). <br/>
+ * User: roman.stumm<br>
+ * Date: 17.06.2010<br>
+ * Time: 09:47:14<br>
+ */
+public class XMLMetaBeanManager extends MetaBeanManager implements XMLMetaBeanRegistry, MetaBeanEnricher {
+    public XMLMetaBeanManager() {
+        this(new XMLMetaBeanBuilder());
+    }
+
+    public XMLMetaBeanManager(XMLMetaBeanBuilder builder) {
+        super(builder);
+    }
+
+    public void addResourceLoader(String resource) {
+        addLoader(new XMLMetaBeanURLLoader(PrivilegedActions.getClassLoader(getClass()).getResource(resource)));
+    }
+
+    public synchronized void addLoader(XMLMetaBeanLoader loader) {
+        ((XMLMetaBeanBuilder) builder).addLoader(loader);
+        cache.clear(); // clear because new loaders can affect ALL MetaBeans
+                       // already created!
+        complete = false;
+    }
+
+    /**
+     * @param infos
+     *            - the patches to apply
+     * @return all MetaBeans for classes that have a xml descriptor and
+     *         additional the MetaBeans loaded by the given loaders. The given
+     *         loaders may also return patches for MetaBeans that have also been
+     *         returned by other loaders. The beans with patches for references
+     *         to patched beans will be copied.
+     */
+    public Map<String, MetaBean> enrichCopies(XMLMetaBeanInfos... infos) {
+        Map<String, MetaBean> cached = findAll();
+        try {
+            Map<String, MetaBean> patched = ((XMLMetaBeanBuilder) builder).enrichCopies(cached, infos);
+            for (Object entry : patched.values()) {
+                MetaBean meta = (MetaBean) entry;
+                computeRelationships(meta, patched);
+            }
+            return patched;
+        } catch (RuntimeException e) {
+            throw e; // do not wrap runtime exceptions
+        } catch (Exception e) {
+            throw new IllegalArgumentException("error enriching beanInfos", e);
+        }
+    }
+
+    /**
+     * 
+     * @return all MetaBeans for classes that have a xml descriptor: key =
+     *         bean.id, value = MetaBean
+     */
+    public Map<String, MetaBean> findAll() {
+        if (!complete) {
+            try {
+                Map<String, MetaBean> allBuilt = builder.buildAll();
+                for (MetaBean meta : allBuilt.values()) {
+                    MetaBean cached = cache.findForId(meta.getId());
+                    if (cached == null) {
+                        cache.cache(meta);
+                    }
+                }
+                Map<String, MetaBean> map = cache.findAll();
+                for (Object oentry : map.values()) {
+                    MetaBean meta = (MetaBean) oentry;
+                    computeRelationships(meta, map);
+                }
+                complete = true;
+                return map;
+            } catch (RuntimeException e) {
+                throw e; // do not wrap runtime exceptions
+            } catch (Exception e) {
+                throw new IllegalArgumentException("error creating beanInfos", e);
+            }
+        } else {
+            return cache.findAll();
+        }
+    }
+
+    protected void computeRelationships(MetaBean beanInfo, Map<String, MetaBean> cached) {
+        for (MetaProperty prop : beanInfo.getProperties()) {
+            String beanRef = (String) prop.getFeature(REF_BEAN_ID);
+            if (beanRef != null) {
+                prop.setMetaBean(cached.get(beanRef));
+            }
+        }
+    }
+
+    protected void computeRelatedMetaBean(MetaProperty prop, String beanRef) {
+        if (beanRef != null) {
+            prop.setMetaBean(findForId(beanRef));
+        } else {
+            super.computeRelatedMetaBean(prop, beanRef);
+        }
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanManagerFactory.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanManagerFactory.java
new file mode 100644
index 0000000..c7910e8
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanManagerFactory.java
@@ -0,0 +1,62 @@
+/*
+ *  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.bval.xml;
+
+import org.apache.bval.MetaBeanFinder;
+
+/**
+ * Description: <br>
+ * User: roman.stumm<br>
+ * Date: 17.06.2010<br>
+ * Time: 10:06:37<br>
+ */
+public class XMLMetaBeanManagerFactory {
+    private static XMLMetaBeanManager manager = new XMLMetaBeanManager();
+     /**
+     * global meta bean finder.
+     * @return
+     */
+    public static MetaBeanFinder getFinder() {
+        return manager;
+    }
+
+
+    /**
+     * set global meta bean manager, that is responsible
+     * for finding, caching, xml registry and enrichment algorithm.
+     * @param finder
+     */
+    public static void setManager(XMLMetaBeanManager finder) {
+        manager = finder;
+    }
+
+    /**
+     * global meta bean registry
+     * @return
+     */
+    public static XMLMetaBeanRegistry getRegistry() {
+        return manager;
+    }
+
+    /**
+     * global meta bean enricher
+     * @return
+     */
+    public static MetaBeanEnricher getEnricher() {
+        return manager;
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanReference.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanReference.java
new file mode 100644
index 0000000..b26fade
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanReference.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.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaProperty;
+
+/**
+ * Description: <br/>
+ */
+@XStreamAlias("relationship")
+public class XMLMetaBeanReference extends XMLMetaElement {
+    /** Serialization version */
+    private static final long serialVersionUID = 1L;
+
+    @XStreamAsAttribute
+    private String beanId;
+
+    public XMLMetaBeanReference(String refId) {
+        this.beanId = refId;
+    }
+
+    public XMLMetaBeanReference() {
+    }
+
+    /** id of referenced target bean of the relationship */
+    public String getBeanId() {
+        return beanId;
+    }
+
+    public void setBeanId(String beanId) {
+        this.beanId = beanId;
+    }
+
+    @Override
+    public void mergeInto(MetaProperty prop) throws ClassNotFoundException {
+        super.mergeInto(prop);   // call super!
+        if (getBeanId() != null) {
+            prop.putFeature(Features.Property.REF_BEAN_ID, getBeanId());
+        }
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanRegistry.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanRegistry.java
new file mode 100644
index 0000000..ef11f98
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanRegistry.java
@@ -0,0 +1,35 @@
+/*
+ *  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.bval.xml;
+
+/**
+ * Description: Interface of the object that holds all XMLMetaBeanLoaders <br/>
+ */
+public interface XMLMetaBeanRegistry {
+    /**
+     * add a loader for xml bean infos.
+     * the registry should use the loader in the sequence they have been added.
+     */
+    void addLoader(XMLMetaBeanLoader loader);
+
+    /**
+     * convenience method to add a loader for a xml file in the classpath
+     *
+     * @param resource - path of xml file in classpath
+     */
+    void addResourceLoader(String resource);
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanURLLoader.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanURLLoader.java
new file mode 100644
index 0000000..8dd735c
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaBeanURLLoader.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.bval.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * Description: <br/>
+ */
+public class XMLMetaBeanURLLoader implements XMLMetaBeanLoader {
+    private final URL url;
+
+    public XMLMetaBeanURLLoader(URL url) {
+        if (url == null) throw new NullPointerException("URL required");
+        this.url = url;
+    }
+
+    public XMLMetaBeanInfos load() throws IOException {
+        InputStream stream = url.openStream();
+        try {
+            XMLMetaBeanInfos beanInfos = (XMLMetaBeanInfos) XMLMapper.getInstance()
+                    .getXStream().fromXML(stream);
+            beanInfos.setId(url.toExternalForm());
+            return beanInfos;
+        } finally {
+            stream.close();
+        }
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaElement.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaElement.java
new file mode 100644
index 0000000..b753a79
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaElement.java
@@ -0,0 +1,143 @@
+/*
+ *  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.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import org.apache.bval.model.MetaProperty;
+import org.apache.commons.lang3.ClassUtils;
+
+import static org.apache.bval.model.Features.Property.*;
+
+/**
+ * Description: <br/>
+ */
+public class XMLMetaElement extends XMLFeaturesCapable {
+    /** Serialization version */
+    private static final long serialVersionUID = 1L;
+
+    @XStreamAsAttribute()
+    private String name;
+    @XStreamAsAttribute()
+    private String mandatory;
+
+    @XStreamAsAttribute()
+    private Integer minLength;
+    @XStreamAsAttribute()
+    private Integer maxLength;
+    @XStreamAsAttribute()
+    private Boolean readonly;
+    @XStreamAsAttribute()
+    private Boolean hidden;
+    @XStreamAsAttribute()
+    private Boolean denied;
+    /**
+     * normally the type is determined by the implementation class.
+     * in case, no implementation class is given, the xml can
+     * contain the type directly.
+     */
+    @XStreamAsAttribute()
+    private String type;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getMandatory() {
+        return mandatory;
+    }
+
+    public void setMandatory(String mandatory) {
+        this.mandatory = mandatory;
+    }
+
+    public Integer getMinLength() {
+        return minLength;
+    }
+
+    public void setMinLength(Integer minLength) {
+        this.minLength = minLength;
+    }
+
+    public Integer getMaxLength() {
+        return maxLength;
+    }
+
+    public void setMaxLength(Integer maxLength) {
+        this.maxLength = maxLength;
+    }
+
+    public Boolean getReadonly() {
+        return readonly;
+    }
+
+    public void setReadonly(Boolean readonly) {
+        this.readonly = readonly;
+    }
+
+    public Boolean getDenied() {
+        return denied;
+    }
+
+    public void setDenied(Boolean denied) {
+        this.denied = denied;
+    }
+
+    public Boolean getHidden() {
+        return hidden;
+    }
+
+    public void setHidden(Boolean hidden) {
+        this.hidden = hidden;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public void mergeInto(MetaProperty prop) throws ClassNotFoundException {
+        mergeFeaturesInto(prop);
+        if (getType() != null && getType().length() > 0) {
+            prop.setType(ClassUtils.getClass(getType())); // enhancement: or use getGenericType() ?
+        }
+        if (getHidden() != null) {
+            prop.putFeature(HIDDEN, getHidden().booleanValue());
+        }
+        if (getMandatory() != null) {
+            prop.putFeature(MANDATORY, getMandatory().equals("true"));
+        }
+        if (getMaxLength() != null) {
+            prop.putFeature(MAX_LENGTH, getMaxLength());
+        }
+        if (getMinLength() != null) {
+            prop.putFeature(MIN_LENGTH, getMinLength());
+        }
+        if (getReadonly() != null) {
+            prop.putFeature(READONLY, getReadonly());
+        }
+        if (getDenied() != null) {
+            prop.putFeature(DENIED, getDenied());
+        }
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaFeature.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaFeature.java
new file mode 100644
index 0000000..a500003
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaFeature.java
@@ -0,0 +1,61 @@
+/*
+ *  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.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+
+import java.io.Serializable;
+
+/**
+ * Description: <br/>
+ */
+@XStreamAlias("feature")
+public class XMLMetaFeature implements Serializable {
+    /** Serialization version */
+    private static final long serialVersionUID = 1L;
+
+    @XStreamAsAttribute
+    private String key;
+    @XStreamAsAttribute
+    private Object value;
+
+    public XMLMetaFeature(String key, Object value) {
+        this.key = key;
+        this.value = value;
+    }
+
+    public XMLMetaFeature() {
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+
+    public Object getValue() {
+        return value;
+    }
+
+    public void setValue(Object value) {
+        this.value = value;
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaProperty.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaProperty.java
new file mode 100644
index 0000000..82c4772
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaProperty.java
@@ -0,0 +1,102 @@
+/*
+ *  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.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import org.apache.bval.model.MetaProperty;
+
+import static org.apache.bval.model.Features.Property.*;
+
+/**
+ * Description: <br/>
+ */
+@XStreamAlias("property")
+public class XMLMetaProperty extends XMLMetaElement {
+    /** Serialization version */
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * if you need other types (Long, double, String) for maxValue and
+     * minValue, configure via
+     * <pre>
+     * &lt;feature key="maxValue">
+     *   &lt;value class="java.lang.Long">1000&lt;/value>
+     * &lt;/feature>
+     * </pre>
+     * instead with explicit typing.
+     */
+    @XStreamAsAttribute()
+    private Integer maxValue; // XStream requires a non-abstract type to parse XML
+    @XStreamAsAttribute()
+    private Integer minValue; // XStream requires a non-abstract type to parse XML
+
+    private String regexp;
+
+    @XStreamAsAttribute()
+    private String timeLag;
+
+
+    public Integer getMaxValue() {
+        return maxValue;
+    }
+
+    public void setMaxValue(Integer maxValue) {
+        this.maxValue = maxValue;
+    }
+
+    public Integer getMinValue() {
+        return minValue;
+    }
+
+    public void setMinValue(Integer minValue) {
+        this.minValue = minValue;
+    }
+
+    public String getRegexp() {
+        return regexp;
+    }
+
+    public void setRegexp(String regexp) {
+        this.regexp = regexp;
+    }
+
+    public String getTimeLag() {
+        return timeLag;
+    }
+
+    public void setTimeLag(String timeLag) {
+        this.timeLag = timeLag;
+    }
+
+    @Override
+    public void mergeInto(MetaProperty prop) throws ClassNotFoundException {
+        super.mergeInto(prop);   // call super!
+        if (getMaxValue() != null) {
+            prop.putFeature(MAX_VALUE, getMaxValue());
+        }
+        if (getMinValue() != null) {
+            prop.putFeature(MIN_VALUE, getMinValue());
+        }
+        if (getRegexp() != null) {
+            prop.putFeature(REG_EXP, getRegexp());
+        }
+        if (getTimeLag() != null) {
+            prop.putFeature(TIME_LAG, getTimeLag());
+        }
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValidator.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValidator.java
new file mode 100644
index 0000000..399d8a4
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValidator.java
@@ -0,0 +1,77 @@
+/*
+ *  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.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import com.thoughtworks.xstream.annotations.XStreamOmitField;
+import org.apache.bval.model.Validation;
+
+import java.io.Serializable;
+
+/**
+ * Description: <br/>
+ */
+@XStreamAlias("validator")
+public class XMLMetaValidator implements Serializable {
+
+    @XStreamOmitField
+    private static final long serialVersionUID = 1L;
+
+    @XStreamAsAttribute
+    private String id;
+    @XStreamAsAttribute
+    private String java;  // implementation of Validation
+
+    @XStreamAsAttribute
+    private String jsFunction; // name of java script function
+
+    @XStreamOmitField
+    private transient Validation validation;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getJava() {
+        return java;
+    }
+
+    public void setJava(String java) {
+        this.java = java;
+    }
+
+    public void setValidation(Validation validation) {
+        this.validation = validation;
+    }
+
+    public Validation getValidation() {
+        return validation;
+    }
+
+    public String getJsFunction() {
+        return jsFunction;
+    }
+
+    public void setJsFunction(String jsFunction) {
+        this.jsFunction = jsFunction;
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValidatorReference.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValidatorReference.java
new file mode 100644
index 0000000..c6627c5
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValidatorReference.java
@@ -0,0 +1,49 @@
+/*
+ *  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.bval.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+
+import java.io.Serializable;
+
+/**
+ * Description: <br/>
+ */
+@XStreamAlias("validator-ref")
+public class XMLMetaValidatorReference implements Serializable {
+    /** Serialization version */
+    private static final long serialVersionUID = 1L;
+
+    @XStreamAsAttribute
+    private String refId;
+
+    public XMLMetaValidatorReference(String id) {
+        this.refId = id;
+    }
+
+    public XMLMetaValidatorReference() {
+    }
+
+    public String getRefId() {
+        return refId;
+    }
+
+    public void setRefId(String refId) {
+        this.refId = refId;
+    }
+}
diff --git a/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValue.java b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValue.java
new file mode 100644
index 0000000..9da06ec
--- /dev/null
+++ b/trunk/bval-xstream/src/main/java/org/apache/bval/xml/XMLMetaValue.java
@@ -0,0 +1,34 @@
+/*
+ *  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.bval.xml;
+
+/**
+ * Description: <br/>
+ */
+public interface XMLMetaValue {
+    // keys for Annotations
+    public static final String ANNOKEY_Widget = "WIDGET";
+    public static final String ANNOKEY_TableColumn = "TABLE_COLUMN";
+
+    // values for TIMELAG
+    public static final String TIMELAG_Past = "PAST";
+    public static final String TIMELAG_Future = "FUTURE";
+
+    // values for MANDATORY
+    public static final String MANDATORY = "true";
+    public static final String OPTIONAL = "false";
+}
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessEnum.java b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessEnum.java
new file mode 100644
index 0000000..035771d
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessEnum.java
@@ -0,0 +1,24 @@
+/*
+ *  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.bval.example;
+
+/**
+ * Description: <br/>
+ */
+public enum BusinessEnum {
+    VALUE1, VALUE2, VALUE3
+}
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObject.java b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObject.java
new file mode 100644
index 0000000..4b511d5
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObject.java
@@ -0,0 +1,125 @@
+/*
+ *  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.bval.example;
+
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Description: <br/>
+ */
+public class BusinessObject {
+    private long userId;
+    private int numericValue;
+    private String firstName, lastName, title;
+    private Date dateBirth;
+    private Timestamp validTo;
+    private String email;
+    private BusinessEnum choice;
+    private BusinessObjectAddress address;
+    private List<BusinessObjectAddress> addresses;
+
+    public BusinessEnum getChoice() {
+        return choice;
+    }
+
+    public void setChoice(BusinessEnum choice) {
+        this.choice = choice;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public Date getDateBirth() {
+        return dateBirth;
+    }
+
+    public void setDateBirth(Date dateBirth) {
+        this.dateBirth = dateBirth;
+    }
+
+    public Timestamp getValidTo() {
+        return validTo;
+    }
+
+    public void setValidTo(Timestamp validTo) {
+        this.validTo = validTo;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public BusinessObjectAddress getAddress() {
+        return address;
+    }
+
+    public void setAddress(BusinessObjectAddress address) {
+        this.address = address;
+    }
+
+    public void setNumericValue(int newNumericValue) {
+        numericValue = newNumericValue;
+    }
+
+    public int getNumericValue() {
+        return numericValue;
+    }
+
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    public String getLastName() {
+        return lastName;
+    }
+
+    public void setLastName(String lastName) {
+        this.lastName = lastName;
+    }
+
+    public long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(long userId) {
+        this.userId = userId;
+    }
+
+    public List<BusinessObjectAddress> getAddresses() {
+        return addresses;
+    }
+
+    public void setAddresses(List<BusinessObjectAddress> addresses) {
+        this.addresses = addresses;
+    }
+
+}
\ No newline at end of file
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObjectAddress.java b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObjectAddress.java
new file mode 100644
index 0000000..9a6c18b
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObjectAddress.java
@@ -0,0 +1,49 @@
+/*
+ *  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.bval.example;
+
+/**
+ * Description: <br/>
+ */
+public class BusinessObjectAddress {
+    private String city, country;
+    private BusinessObject owner;
+
+    public String getCity() {
+        return city;
+    }
+
+    public void setCity(String city) {
+        this.city = city;
+    }
+
+    public String getCountry() {
+        return country;
+    }
+
+    public void setCountry(String country) {
+        this.country = country;
+    }
+
+    public BusinessObject getOwner() {
+        return owner;
+    }
+
+    public void setOwner(BusinessObject owner) {
+        this.owner = owner;
+    }
+}
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObjectBeanInfo.java b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObjectBeanInfo.java
new file mode 100644
index 0000000..61b06e5
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/example/BusinessObjectBeanInfo.java
@@ -0,0 +1,64 @@
+/*
+ *  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.bval.example;
+
+import org.apache.bval.model.Features;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.beans.SimpleBeanInfo;
+
+/**
+ * Description: <br/>
+ */
+public class BusinessObjectBeanInfo extends SimpleBeanInfo {
+    Class<?> targetClass = BusinessObject.class;
+
+    @Override
+    public BeanInfo[] getAdditionalBeanInfo() {
+        ExplicitBeanInfo bi = new ExplicitBeanInfo();
+        bi.setPropertyDescriptors(_getPropertyDescriptors());
+        return new BeanInfo[]{bi};
+    }
+
+    public PropertyDescriptor[] _getPropertyDescriptors() {
+        try {
+            PropertyDescriptor numericValue = new PropertyDescriptor("numericValue",
+                    targetClass, "getNumericValue", "setNumericValue");
+            numericValue.setValue(Features.Property.MAX_VALUE, new Integer(100));
+            numericValue.setValue(Features.Property.MIN_VALUE, new Integer(-100));
+            return new PropertyDescriptor[]{numericValue};
+        } catch (IntrospectionException ex) {
+            ex.printStackTrace();
+            return null;
+        }
+    }
+}
+
+class ExplicitBeanInfo extends SimpleBeanInfo {
+    private PropertyDescriptor[] propertyDescriptors;
+
+    @Override
+    public PropertyDescriptor[] getPropertyDescriptors() {
+        return propertyDescriptors;
+    }
+
+    public void setPropertyDescriptors(PropertyDescriptor[] propertyDescriptors) {
+        this.propertyDescriptors = propertyDescriptors;
+    }
+}
\ No newline at end of file
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/routines/StandardValidationTest.java b/trunk/bval-xstream/src/test/java/org/apache/bval/routines/StandardValidationTest.java
new file mode 100644
index 0000000..7bee3db
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/routines/StandardValidationTest.java
@@ -0,0 +1,223 @@
+/*
+ *  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.bval.routines;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.BeanValidationContext;
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaProperty;
+import org.apache.bval.model.ValidationContext;
+import org.apache.bval.model.ValidationListener;
+import org.apache.bval.xml.XMLMetaValue;
+
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * StandardValidation Tester.
+ */
+public class StandardValidationTest extends TestCase implements ValidationListener {
+    private StandardValidation validation;
+    private BeanValidationContext<StandardValidationTest> context;
+    private List<String> reasons = new ArrayList<String>();
+    private MetaProperty metaProperty;
+    private String stringValue;
+    private Date dateValue;
+    private int intValue;
+
+    public StandardValidationTest(String name) {
+        super(name);
+    }
+
+    public void setUp() throws Exception {
+        super.setUp();
+        validation = new StandardValidation();
+        context = new BeanValidationContext<StandardValidationTest>(this);
+        metaProperty = new MetaProperty();
+        context.setBean(this, null);
+        context.setMetaProperty(metaProperty);
+    }
+
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public String getStringValue() {
+        return stringValue;
+    }
+
+    public void testValidateMandatory() {
+        metaProperty.setName("stringValue");
+
+        // test not-null value that is mandatory
+        metaProperty.setMandatory(true);
+        stringValue = "some value";
+        validation.validateMandatory(context);
+        assertTrue(reasons.isEmpty());
+
+        // test null value that is mandatory
+        context.unknownValue();
+        stringValue = null;
+        validation.validateMandatory(context);
+        assertTrue(reasons.contains(Reasons.MANDATORY));
+
+        // test null value that is NOT mandatory
+        context.unknownValue();
+        metaProperty.setMandatory(false);
+        reasons.clear();
+        validation.validateMandatory(context);
+        assertTrue(reasons.isEmpty());
+    }
+
+    public void testValidateMaxLength() {
+        metaProperty.setName("stringValue");
+        metaProperty.putFeature(Features.Property.MAX_LENGTH, 5);
+        stringValue = "1234";
+        validation.validateMaxLength(context);
+        assertTrue(reasons.isEmpty());
+        context.unknownValue();
+        stringValue = "much too long";
+        validation.validateMaxLength(context);
+        assertTrue(reasons.contains(Reasons.MAX_LENGTH));
+    }
+
+    public void testValidateMinLength() {
+        metaProperty.setName("stringValue");
+        metaProperty.putFeature(Features.Property.MIN_LENGTH, 5);
+        stringValue = "123456";
+        validation.validateMinLength(context);
+        assertTrue(reasons.isEmpty());
+        context.unknownValue();
+        stringValue = "123";
+        validation.validateMinLength(context);
+        assertTrue(reasons.contains(Reasons.MIN_LENGTH));
+    }
+
+    public void testValidateMaxValue() {
+        metaProperty.setName("stringValue");
+        metaProperty.putFeature(Features.Property.MAX_VALUE, "9999");
+        stringValue = "1111";
+        validation.validateMaxValue(context);
+        assertTrue(reasons.isEmpty());
+        context.unknownValue();
+        stringValue = "99999";
+        validation.validateMaxValue(context);
+        assertTrue(reasons.contains(Reasons.MAX_VALUE));
+    }
+
+    public void testValidateMinValue() {
+        metaProperty.setName("stringValue");
+        metaProperty.putFeature(Features.Property.MIN_VALUE, "5555");
+        stringValue = "8888";
+        validation.validateMinValue(context);
+        assertTrue(reasons.isEmpty());
+        context.unknownValue();
+        stringValue = "3333";
+        validation.validateMinValue(context);
+        assertTrue(reasons.contains(Reasons.MIN_VALUE));
+    }
+
+    public int getIntValue() {
+        return intValue;
+    }
+
+    public void testValidateMinValue_MixedNumber() {
+        metaProperty.setName("intValue");
+        metaProperty.putFeature(Features.Property.MIN_VALUE, new Long(0));
+        intValue = 5;
+        validation.validateMinValue(context);
+        assertTrue(reasons.isEmpty());
+        context.unknownValue();
+        intValue = -1;
+        validation.validateMinValue(context);
+        assertTrue(reasons.contains(Reasons.MIN_VALUE));
+    }
+
+    public void testValidateMinValue_Date_Timestamp() {
+        metaProperty.setName("dateValue");
+        Date dt = new Date();
+        metaProperty.putFeature(Features.Property.MIN_VALUE, dt);
+        dateValue = new Timestamp(dt.getTime()+1000);
+        validation.validateMinValue(context);
+        assertTrue(reasons.isEmpty());
+        context.unknownValue();
+        dateValue = new Timestamp(dt.getTime()-1000);
+        validation.validateMinValue(context);
+        assertTrue(reasons.contains(Reasons.MIN_VALUE));
+    }
+
+    public void testValidateMaxValue_AlphabeticString() {
+        metaProperty.setName("stringValue");
+        metaProperty.putFeature(Features.Property.MAX_VALUE, "BBBB");
+        stringValue = "AAAA";
+        validation.validateMaxValue(context);
+        assertTrue(reasons.isEmpty());
+        context.unknownValue();
+        stringValue = "BBBC";
+        validation.validateMaxValue(context);
+        assertTrue(reasons.contains(Reasons.MAX_VALUE));
+    }
+
+    public void testValidateRegExp() {
+        // regexp for Zip
+        String regexp = "[a-zA-Z\\- \\d]*";
+        metaProperty.setName("stringValue");
+        metaProperty.putFeature(Features.Property.REG_EXP, regexp);
+        stringValue = "53773";
+        validation.validateRegExp(context);
+        assertTrue(reasons.isEmpty());
+        context.unknownValue();
+        stringValue = "5355/7"; // invalid zip value
+        validation.validateRegExp(context);
+        assertTrue(reasons.contains(Reasons.REG_EXP));
+    }
+
+    public Date getDateValue() {
+        return dateValue;
+    }
+
+    public void testValidateTimeLag() {
+        metaProperty.setName("dateValue");
+        metaProperty.putFeature(Features.Property.TIME_LAG, XMLMetaValue.TIMELAG_Past);
+
+        dateValue = new Date(System.currentTimeMillis() - 10000);
+        validation.validateTimeLag(context);
+        assertTrue(reasons.isEmpty());
+
+        metaProperty.putFeature(Features.Property.TIME_LAG, XMLMetaValue.TIMELAG_Future);
+        validation.validateTimeLag(context);
+        assertTrue(reasons.contains(Reasons.TIME_LAG));
+
+    }
+
+    public static Test suite() {
+        return new TestSuite(StandardValidationTest.class);
+    }
+
+    public <T extends ValidationListener> void addError(String reason, ValidationContext<T> context) {
+        reasons.add(reason);
+    }
+
+    public <T extends ValidationListener> void addError(ValidationListener.Error error, ValidationContext<T> context) {
+        reasons.add(error.getReason());
+    }
+
+}
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/xml/BeanValidatorTest.java b/trunk/bval-xstream/src/test/java/org/apache/bval/xml/BeanValidatorTest.java
new file mode 100644
index 0000000..956da15
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/xml/BeanValidatorTest.java
@@ -0,0 +1,121 @@
+/*
+ *  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.bval.xml;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.BeanValidator;
+import org.apache.bval.MetaBeanFinder;
+import org.apache.bval.ValidationResults;
+import org.apache.bval.example.BusinessObject;
+import org.apache.bval.example.BusinessObjectAddress;
+import org.apache.bval.model.Features;
+import org.apache.bval.model.MetaBean;
+import org.apache.bval.routines.Reasons;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * BeanValidator Tester.
+ */
+public class BeanValidatorTest extends TestCase {
+    public BeanValidatorTest(String name) {
+        super(name);
+    }
+
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testValidateMapAsBean() {
+        XMLMetaBeanManagerFactory.getRegistry().addLoader(new XMLMetaBeanURLLoader(
+              BusinessObject.class.getResource("test-beanInfos.xml")));
+
+        MetaBean mb = XMLMetaBeanManagerFactory.getFinder()
+              .findForId("org.apache.bval.example.Address");
+
+        // 1. validate a bean
+        BusinessObjectAddress adr = new BusinessObjectAddress();
+        BeanValidator<ValidationResults> validator = new BeanValidator<ValidationResults>();
+        ValidationResults results = validator.validate(adr, mb);
+        assertEquals(2,
+              results.getErrorsByReason().get(Features.Property.MANDATORY).size());
+
+        // 2. validate a map with the same metabean
+        validator.setTreatMapsLikeBeans(true);
+        results = validator.validate(new HashMap<String, Object>(), mb);
+        assertFalse(results.isEmpty());
+        assertEquals(2,
+              results.getErrorsByReason().get(Features.Property.MANDATORY).size());
+
+        // 3. validate as empty map (jsr303 behavior)
+        validator.setTreatMapsLikeBeans(false);
+        results = validator.validate(new HashMap<Object, Object>(), mb);
+        assertTrue(results.isEmpty());
+    }
+
+    public void testValidate() {
+        MetaBeanFinder finder = XMLMetaBeanManagerFactory.getFinder();
+        XMLMetaBeanManagerFactory.getRegistry().addLoader(new XMLMetaBeanURLLoader(
+              BusinessObject.class.getResource("test-beanInfos.xml")));
+        MetaBean info = finder.findForClass(BusinessObject.class);
+        BusinessObject object = new BusinessObject();
+        object.setAddress(new BusinessObjectAddress());
+        object.getAddress().setOwner(object);
+        BeanValidator<ValidationResults> validator = new BeanValidator<ValidationResults>();
+        ValidationResults results = validator.validate(object, info);
+        assertTrue(results.hasErrorForReason(Reasons.MANDATORY));
+        assertTrue(results.hasError(object, null));
+        assertTrue(results.hasError(object.getAddress(), null));
+
+        assertTrue(
+              validator.validateProperty(object, info.getProperty("firstName")).hasError(
+                    object, "firstName"));
+
+        object.setUserId(1L);
+        object.setFirstName("Hans");
+        object.setLastName("Tester");
+        object.setAddress(new BusinessObjectAddress());
+        object.getAddress().setOwner(object);
+        assertFalse(validator.validate(object, info).isEmpty());
+
+        object.getAddress().setCountry("0123456789012345678");
+        assertFalse(validator.validate(object, info).isEmpty());
+
+        object.getAddress().setCountry("Germany");
+        object.setAddresses(new ArrayList<BusinessObjectAddress>());
+        object.getAddresses().add(object.getAddress());
+        object.getAddresses().add(object.getAddress());
+        object.getAddresses().add(object.getAddress());
+        assertTrue(validator.validate(object, info).isEmpty());
+
+        // 4th address is too much!
+        object.getAddresses().add(object.getAddress());
+        assertFalse(
+              validator.validate(object, info).isEmpty()); // cardinality error found
+    }
+
+    public static Test suite() {
+        return new TestSuite(BeanValidatorTest.class);
+    }
+}
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/xml/XMLMetaBeanInfosTest.java b/trunk/bval-xstream/src/test/java/org/apache/bval/xml/XMLMetaBeanInfosTest.java
new file mode 100644
index 0000000..79fd3dc
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/xml/XMLMetaBeanInfosTest.java
@@ -0,0 +1,165 @@
+/*
+ *  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.bval.xml;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.example.BusinessObject;
+import org.apache.bval.example.BusinessObjectAddress;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * XMLMetaBean Tester.
+ */
+public class XMLMetaBeanInfosTest extends TestCase {
+
+    public XMLMetaBeanInfosTest(String name) {
+        super(name);
+    }
+
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testBeanInfosToXML() {
+        XMLMetaBeanInfos infos = new XMLMetaBeanInfos();
+        infos.setBeans(new ArrayList<XMLMetaBean>());
+        infos.setValidators(new ArrayList<XMLMetaValidator>());
+
+        XMLMetaValidator validator = new XMLMetaValidator();
+        validator.setId("mandatory");
+        validator.setJava("org.apache.bval.MandatoryValidator");
+
+        infos.getValidators().add(validator);
+
+        validator = new XMLMetaValidator();
+        validator.setId("email");
+        validator.setJava("org.apache.bval.EMailValidation");
+
+        infos.getValidators().add(validator);
+
+        XMLMetaBean bean = new XMLMetaBean();
+        bean.putFeature("DOMAIN", "TestProfile");
+        bean.putFeature("label-key", "business-object-label");
+        bean.setId("User");
+        bean.setImpl(BusinessObject.class.getName());
+        bean.setProperties(new ArrayList<XMLMetaProperty>());
+        XMLMetaProperty property = new XMLMetaProperty();
+        property.setName("userId");
+        property.setMandatory(XMLMetaValue.MANDATORY);
+        bean.getProperties().add(property);
+
+        property = new XMLMetaProperty();
+        property.setName("firstName");
+        property.setMandatory(XMLMetaValue.MANDATORY);
+        property.setMaxLength(100);
+        bean.getProperties().add(property);
+
+        property = new XMLMetaProperty();
+        property.setName("lastName");
+        property.setMandatory(XMLMetaValue.MANDATORY);
+        property.setMaxLength(100);
+        bean.getProperties().add(property);
+
+        property = new XMLMetaProperty();
+        property.setName("title");
+        property.setMandatory(XMLMetaValue.OPTIONAL);
+        property.setMaxLength(10);
+        bean.getProperties().add(property);
+
+        property = new XMLMetaProperty();
+        property.setName("dateBirth");
+        property.setMandatory(XMLMetaValue.OPTIONAL);
+        property.setTimeLag(XMLMetaValue.TIMELAG_Past);
+        bean.getProperties().add(property);
+
+        property = new XMLMetaProperty();
+        property.setName("validTo");
+        property.setMandatory(XMLMetaValue.OPTIONAL);
+        property.setTimeLag(XMLMetaValue.TIMELAG_Future);
+        bean.getProperties().add(property);
+
+        property = new XMLMetaProperty();
+        property.setName("email");
+        property.putFeature(XMLMetaValue.ANNOKEY_Widget, "entry");
+        property.putFeature(XMLMetaValue.ANNOKEY_TableColumn, true);
+        Map<String, String> formatterMap = new HashMap<String, String>();
+        formatterMap.put("locale", "DE");
+        formatterMap.put("style", "info");
+        property.putFeature("ajax-formatter", formatterMap);
+        property.addValidator("email");
+        bean.getProperties().add(property);
+
+        infos.getBeans().add(bean);
+
+        XMLMetaBean bean2 = new XMLMetaBean();
+        bean2.setId("Address");
+        bean2.setImpl(BusinessObjectAddress.class.getName());
+        property = new XMLMetaProperty();
+        property.setName("city");
+        bean2.putProperty(property);
+        property = new XMLMetaProperty();
+        property.setName("country");
+        property.setMaxLength(10);
+        property.setMandatory(XMLMetaValue.MANDATORY);
+        bean2.putProperty(property);
+
+        XMLMetaBeanReference relation = new XMLMetaBeanReference();
+        relation.setName("address");
+        relation.setBeanId("Address");
+        relation.setMandatory(XMLMetaValue.OPTIONAL);
+//        relation.setDisplayName("UserAddress");
+        bean.putBeanRef(relation);
+
+        infos.getBeans().add(bean2);
+
+        String xml = XMLMapper.getInstance().getXStream().toXML(infos);
+//        System.out.println(xml);
+        XMLMetaBeanInfos infos2 =
+              (XMLMetaBeanInfos) XMLMapper.getInstance().getXStream().fromXML(xml);
+        assertEquals(2, infos2.getBeans().size());
+    }
+
+
+    public void testMaxValueParsing() {
+        String xml = "\n" +
+              "<beanInfos>  <bean id=\"org.apache.bval.test.model.Profile\">\n" +
+              "    <property name=\"activationDay\" minValue=\"1\" maxValue=\"31\"/>\n" +
+              "    <property name=\"activationMonth\" minValue=\"1\" maxValue=\"12\"/>\n" +
+              "  </bean></beanInfos>";
+        XMLMetaBeanInfos beanInfos = (XMLMetaBeanInfos) XMLMapper.getInstance()
+              .getXStream().fromXML(xml);
+        Assert.assertNotNull(beanInfos);
+        assertEquals(Integer.valueOf(31),
+              beanInfos.getBeans().get(0).getProperty("activationDay").getMaxValue());
+        assertEquals(Integer.valueOf(1),
+              beanInfos.getBeans().get(0).getProperty("activationDay").getMinValue());
+    }
+
+    public static Test suite() {
+        return new TestSuite(XMLMetaBeanInfosTest.class);
+    }
+}
diff --git a/trunk/bval-xstream/src/test/java/org/apache/bval/xml/XMLMetaBeanManagerTest.java b/trunk/bval-xstream/src/test/java/org/apache/bval/xml/XMLMetaBeanManagerTest.java
new file mode 100644
index 0000000..a57c306
--- /dev/null
+++ b/trunk/bval-xstream/src/test/java/org/apache/bval/xml/XMLMetaBeanManagerTest.java
@@ -0,0 +1,93 @@
+/*
+ *  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.bval.xml;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.bval.MetaBeanFinder;
+import org.apache.bval.example.BusinessObject;
+import org.apache.bval.model.MetaBean;
+
+import java.util.Map;
+
+/**
+ * Description: <br>
+ * User: roman.stumm<br>
+ * Date: 17.06.2010<br>
+ * Time: 10:28:48<br>
+ */
+public class XMLMetaBeanManagerTest extends TestCase {
+    XMLMetaBeanManager mbm = new XMLMetaBeanManager();
+
+    public XMLMetaBeanManagerTest(String name) {
+        super(name);
+    }
+
+    public void setUp() throws Exception {
+        super.setUp();
+        mbm.addLoader(new XMLMetaBeanURLLoader(BusinessObject.class.getResource("test-beanInfos.xml")));
+    }
+
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testEnrichCopies() throws Exception {
+        Map<String, MetaBean> copies =
+            mbm.enrichCopies(new XMLMetaBeanURLLoader(BusinessObject.class.getResource("test-beanInfos-custom.xml"))
+                .load());
+        assertNotNull(copies);
+        MetaBean mb = copies.get(BusinessObject.class.getName());
+        assertFalse(mb.getProperty("lastName").isMandatory());
+        MetaBean mb2 = mbm.findForClass(BusinessObject.class);
+        assertTrue(mb2.getProperty("lastName").isMandatory());
+    }
+
+    public void testCopy() {
+        MetaBean mb = mbm.findForClass(BusinessObject.class);
+        MetaBean mb2 = mb.copy();
+        assertTrue(mb2 != mb);
+        assertTrue(mb2.getProperty("dateBirth") != mb.getProperty("dateBirth"));
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testFindForClass() throws Exception {
+        MetaBeanFinder finder = mbm;
+        MetaBean info = finder.findForClass(BusinessObject.class);
+        assertNotNull(info);
+        assertTrue(info == info.getProperty("address").getMetaBean().getProperty("owner").getMetaBean());
+        assertTrue(info == info.getProperty("addresses").getMetaBean().getProperty("owner").getMetaBean());
+        assertTrue(info.getProperty("email").getJavaScriptValidations().length > 0);
+    }
+
+    public void testFindAll() {
+        Map<String, MetaBean> all = mbm.findAll();
+        assertNotNull(all);
+        Map<String, MetaBean> all2 = mbm.findAll();
+        assertEquals(all.size(), all2.size());
+        assertTrue(all.get(BusinessObject.class.getName()) == all2.get(BusinessObject.class.getName()));
+        assertTrue(all.get(BusinessObject.class.getName()) != null);
+        MetaBean bean = all.get(BusinessObject.class.getName());
+        assertTrue(bean == bean.getProperty("address").getMetaBean().getProperty("owner").getMetaBean());
+        assertTrue(bean == bean.getProperty("addresses").getMetaBean().getProperty("owner").getMetaBean());
+    }
+
+    public static Test suite() {
+        return new TestSuite(XMLMetaBeanManagerTest.class);
+    }
+}
diff --git a/trunk/bval-xstream/src/test/resources/org/apache/bval/example/test-beanInfos-custom.xml b/trunk/bval-xstream/src/test/resources/org/apache/bval/example/test-beanInfos-custom.xml
new file mode 100644
index 0000000..d9f6b72
--- /dev/null
+++ b/trunk/bval-xstream/src/test/resources/org/apache/bval/example/test-beanInfos-custom.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<beanInfos>
+
+  <bean id="org.apache.bval.example.BusinessObject"
+        impl="org.apache.bval.example.BusinessObject">
+    <property name="lastName" mandatory="false"/>
+    <property name="unknownName" mandatory="true"/>
+  </bean>
+
+  <bean id="UnknownObject" impl="org.apache.bval.UnknownClass">
+    <property name="fullName" mandatory="true"/>
+  </bean>
+</beanInfos>
\ No newline at end of file
diff --git a/trunk/bval-xstream/src/test/resources/org/apache/bval/example/test-beanInfos.xml b/trunk/bval-xstream/src/test/resources/org/apache/bval/example/test-beanInfos.xml
new file mode 100644
index 0000000..f78aa47
--- /dev/null
+++ b/trunk/bval-xstream/src/test/resources/org/apache/bval/example/test-beanInfos.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<beanInfos>
+  <validator id="standard" java="org.apache.bval.routines.NOPValidation"
+             jsFunction="noop"/>
+  <validator id="email" java="org.apache.bval.routines.EMailValidation"/>
+  <bean id="org.apache.bval.example.BusinessObject"
+        impl="org.apache.bval.example.BusinessObject">
+    <feature key="mainKey">
+      <value class="string">userId</value>
+    </feature>
+    <feature key="DOMAIN">
+      <value class="string">TestObjects</value>
+    </feature>
+    <feature key="WIDGET">
+      <value class="string">entry</value>
+    </feature>
+    <property name="userId" mandatory="true"/>
+    <property name="firstName" mandatory="true" maxLength="100"/>
+    <property name="lastName" mandatory="true" maxLength="100"/>
+    <property name="title" mandatory="false" maxLength="10"/>
+    <property timeLag="PAST" name="dateBirth" mandatory="false"/>
+    <property timeLag="FUTURE" name="validTo" mandatory="false"/>
+    <property name="email">
+      <feature key="WIDGET">
+        <value class="string">entry</value>
+      </feature>
+      <feature key="TABLE_COLUMN">
+        <value class="boolean">true</value>
+      </feature>
+      <feature key="ajax-formatter">
+        <value class="map">
+          <entry>
+            <string>locale</string>
+            <string>DE</string>
+          </entry>
+          <entry>
+            <string>style</string>
+            <string>info</string>
+          </entry>
+        </value>
+      </feature>
+      <validator refId="email"/>
+      <validator refId="standard"/>
+    </property>
+    <relationship beanId="org.apache.bval.example.Address" name="address" mandatory="false"
+                  displayName="UserAddress"/>
+    <relationship beanId="org.apache.bval.example.Address" name="addresses" maxLength="3"/>
+  </bean>
+  <bean id="org.apache.bval.example.Address"
+        impl="org.apache.bval.example.BusinessObjectAddress">
+    <feature key="DOMAIN">
+      <value class="string">TestObjects</value>
+    </feature>
+    <property name="city"/>
+    <property name="country" mandatory="true" maxLength="10"/>
+    <relationship name="owner" beanId="org.apache.bval.example.BusinessObject"
+                  mandatory="true"/>
+  </bean>
+</beanInfos>
\ No newline at end of file
diff --git a/trunk/pom.xml b/trunk/pom.xml
new file mode 100644
index 0000000..3eba1b4
--- /dev/null
+++ b/trunk/pom.xml
@@ -0,0 +1,624 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+--><!--
+	Maven release plugin requires the project tag to be on a single line.
+--><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache</groupId>
+        <artifactId>apache</artifactId>
+        <version>10</version>
+        <relativePath />
+    </parent>
+
+    <groupId>org.apache.bval</groupId>
+    <artifactId>bval-parent</artifactId>
+    <name>Apache BVal :: bval-parent (Parent POM)</name>
+    <packaging>pom</packaging>
+    <version>0.4</version>
+
+    <description>Apache BVal parent pom</description>
+
+    <properties>
+        <site.deploy.url>scp://people.apache.org/home/${user.name}/public_html/bval/${project.version}/staging-site</site.deploy.url>
+    </properties>
+
+    <inceptionYear>2010</inceptionYear>
+
+    <url>http://bval.apache.org/</url>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/bval/tags/0.4</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/bval/tags/0.4</developerConnection>
+        <url>http://svn.apache.org/repos/asf/bval/tags/0.4</url>
+    </scm>
+
+    <mailingLists>
+        <mailingList>
+            <name>BVal User List</name>
+            <subscribe>user-subscribe@bval.apache.org</subscribe>
+            <unsubscribe>user-unsubscribe@bval.apache.org</unsubscribe>
+            <post>mailto:user@bval.apache.org</post>
+            <archive>http://mail-archives.apache.org/mod_mbox/bval-user/</archive>
+        </mailingList>
+
+        <mailingList>
+            <name>BVal Developer List</name>
+            <subscribe>dev-subscribe@bval.apache.org</subscribe>
+            <unsubscribe>dev-unsubscribe@bval.apache.org</unsubscribe>
+            <post>mailto:dev@bval.apache.org</post>
+            <archive>http://mail-archives.apache.org/mod_mbox/bval-dev/</archive>
+        </mailingList>
+
+        <mailingList>
+            <name>BVal Commits List</name>
+            <subscribe>commits-subscribe@bval.apache.org</subscribe>
+            <unsubscribe>commits-unsubscribe@bval.apache.org</unsubscribe>
+            <post>mailto:commits@bval.apache.org</post>
+            <archive>http://mail-archives.apache.org/mod_mbox/bval-commits/</archive>
+        </mailingList>
+    </mailingLists>
+
+    <issueManagement>
+        <url>https://issues.apache.org/jira/browse/BVAL</url>
+    </issueManagement>
+
+    <developers>
+        <developer>
+            <id>allee8285</id>
+            <name>Albert Lee</name>
+            <email>allee8285@apache.org</email>
+            <roles>
+                <role>PMC Member</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>carlosvara</id>
+            <name>Carlos Vara</name>
+            <email>carlosvara@apache.org</email>
+            <roles>
+                <role>PMC Member</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>djencks</id>
+            <name>David Jencks</name>
+            <email>djencks@apache.org</email>
+            <organization>IBM</organization>
+            <roles>
+                <role>PMC Member</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>dwoods</id>
+            <name>Donald Woods</name>
+            <email>dwoods@apache.org</email>
+            <organization>IBM</organization>
+            <roles>
+                <role>PMC Member</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>gpetracek</id>
+            <name>Gerhard Petracek</name>
+            <email>gpetracek@apache.org</email>
+            <organization>IRIAN Solutions GmbH</organization>
+            <roles>
+                <role>PMC Member</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>jrbauer</id>
+            <name>Jeremy Bauer</name>
+            <email>jrbauer@apache.org</email>
+            <organization>IBM</organization>
+            <roles>
+                <role>PMC Member</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>kevan</id>
+            <name>Kevan Miller</name>
+            <email>kevan@apache.org</email>
+            <organization>IBM</organization>
+            <roles>
+                <role>PMC Member</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>lresende</id>
+            <name>Luciano Resende</name>
+            <email>lresende@apache.org</email>
+            <organization>IBM</organization>
+            <roles>
+                <role>PMC Member</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>struberg</id>
+            <name>Mark Struberg</name>
+            <email>struberg@apache.org</email>
+            <roles>
+                <role>PMC Member</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>mbenson</id>
+            <name>Matt Benson</name>
+            <email>mbenson@apache.org</email>
+            <roles>
+                <role>PMC Member</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>matzew</id>
+            <name>Matthias Wessendorf</name>
+            <email>matzew@apache.org</email>
+            <organization>Kaazing</organization>
+            <roles>
+                <role>PMC Member</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>mnour</id>
+            <name>Mohammad Nour El-Din</name>
+            <email>mnour@apache.org</email>
+            <organization>Thebe Technology</organization>
+            <roles>
+                <role>PMC Member</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>romanstumm</id>
+            <name>Roman Stumm</name>
+            <email>romanstumm@apache.org</email>
+            <organization>Viaboxx GmbH</organization>
+            <roles>
+                <role>PMC Member</role>
+            </roles>
+        </developer>
+        <developer>
+            <id>simonetripodi</id>
+            <name>Simone Tripodi</name>
+            <email>simonetripodi@apache.org</email>
+            <roles>
+                <role>PMC Member</role>
+            </roles>
+        </developer>
+    </developers>
+
+    <distributionManagement>
+        <site>
+            <id>people.apache.org</id>
+            <url>${site.deploy.url}</url>
+        </site>
+    </distributionManagement>
+
+    <reporting>
+        <plugins>
+            <plugin>
+                <artifactId>maven-project-info-reports-plugin</artifactId>
+                <version>2.1.1</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <show>package</show>
+                    <quiet>true</quiet>
+                    <source>1.5</source>
+                    <links>
+                        <link>http://java.sun.com/j2se/1.5.0/docs/api</link>
+                        <link>http://java.sun.com/javaee/6/docs/api</link>
+                    </links>
+                </configuration>
+                <reportSets>
+                    <reportSet>
+                        <reports>
+                            <report>javadoc</report>
+                            <report>test-javadoc</report>
+                        </reports>
+                    </reportSet>
+                </reportSets>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>jdepend-maven-plugin</artifactId>
+                <version>2.0-beta-2</version>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <version>2.3.2</version>
+            </plugin>
+        </plugins>
+    </reporting>
+
+    <!--
+        default versions for dependencies that child modules may include
+    -->
+    <dependencyManagement>
+        <dependencies>
+            <!-- Default of Apache Geronimo version of the Spec API -->
+            <dependency>
+                <groupId>org.apache.geronimo.specs</groupId>
+                <artifactId>geronimo-validation_1.0_spec</artifactId>
+                <version>1.1</version>
+            </dependency>
+            <!-- Optional profile to use Spec RI API -->
+            <dependency>
+                <groupId>javax.validation</groupId>
+                <artifactId>validation-api</artifactId>
+                <version>1.0.0.GA</version>
+            </dependency>
+            <!-- JPA2 spec required for JPA TraversableResolver support -->
+            <dependency>
+                <groupId>org.apache.geronimo.specs</groupId>
+                <artifactId>geronimo-jpa_2.0_spec</artifactId>
+                <version>1.1</version>
+            </dependency>
+            <dependency>
+                <groupId>junit</groupId>
+                <artifactId>junit</artifactId>
+                <version>4.8.2</version>
+            </dependency>
+            <dependency>
+                <groupId>org.mockito</groupId>
+                <artifactId>mockito-core</artifactId>
+                <version>1.8.5</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.commons</groupId>
+                <artifactId>commons-lang3</artifactId>
+                <version>3.1</version>
+            </dependency>
+            <dependency>
+                <groupId>commons-beanutils</groupId>
+                <artifactId>commons-beanutils-core</artifactId>
+                <version>1.8.3</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>commons-logging</groupId>
+                        <artifactId>commons-logging</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
+                <groupId>com.thoughtworks.xstream</groupId>
+                <artifactId>xstream</artifactId>
+                <version>1.3.1</version>
+            </dependency>
+            <!-- Optional - only used by bval-json -->
+            <dependency>
+                <groupId>org.freemarker</groupId>
+                <artifactId>freemarker</artifactId>
+                <version>2.3.15</version>
+            </dependency>
+            <!-- Optional - only used by bval-guice -->
+            <dependency>
+                <groupId>javax.inject</groupId>
+                <artifactId>javax.inject</artifactId>
+                <version>1</version>
+            </dependency>
+            <dependency>
+                <groupId>com.google.inject</groupId>
+                <artifactId>guice</artifactId>
+                <version>3.0-rc2</version>
+            </dependency>
+            <dependency>
+                <groupId>aopalliance</groupId>
+                <artifactId>aopalliance</artifactId>
+                <version>1.0</version>
+            </dependency>
+            <!-- Added mainly for testing -->
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>jcl-over-slf4j</artifactId>
+                <version>1.6.1</version>
+            </dependency>
+            <dependency>
+                <groupId>org.slf4j</groupId>
+                <artifactId>slf4j-simple</artifactId>
+                <version>1.6.1</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <build>
+        <defaultGoal>install</defaultGoal>
+
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+            </resource>
+        </resources>
+
+        <testResources>
+            <testResource>
+                <directory>src/test/resources</directory>
+            </testResource>
+        </testResources>
+
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <configuration>
+                        <source>1.5</source>
+                        <target>1.5</target>
+                        <encoding>${project.build.sourceEncoding}</encoding>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-jar-plugin</artifactId>
+                    <configuration>
+                        <archive>
+                            <manifest>
+                                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                            </manifest>
+                            <manifestEntries>
+                                <Implementation-Build>${buildNumber}</Implementation-Build>
+                                <Specification-Title>JSR-303 Bean Validation</Specification-Title>
+                                <Specification-Vendor>Sun Microsystems, Inc.</Specification-Vendor>
+                                <Specification-Version>1.0</Specification-Version>
+                            </manifestEntries>
+                        </archive>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-javadoc-plugin</artifactId>
+                    <configuration>
+                        <encoding>${project.build.sourceEncoding}</encoding>
+                        <additionalparam>${javadoc.additionalparam}</additionalparam>
+                        <aggregate>true</aggregate>
+                        <subpackages>org.apache</subpackages>
+                        <verbose>false</verbose>
+                        <maxmemory>512m</maxmemory>
+                        <links>
+                            <link>http://java.sun.com/j2se/1.5.0/docs/api</link>
+                            <link>http://java.sun.com/javaee/6/docs/api</link>
+                        </links>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-remote-resources-plugin</artifactId>
+                    <executions>
+                        <execution>
+                            <id>process-remote-resources</id>
+                            <goals>
+                                <goal>process</goal>
+                            </goals>
+                            <configuration>
+                                <resourceBundles>
+                                    <resourceBundle>org.apache:apache-jar-resource-bundle:1.4</resourceBundle>
+                                </resourceBundles>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>jdepend-maven-plugin</artifactId>
+                    <version>2.0-beta-2</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.felix</groupId>
+                    <artifactId>maven-bundle-plugin</artifactId>
+                    <version>2.0.1</version>
+                    <extensions>true</extensions>
+                    <configuration>
+                        <instructions>
+                            <Implementation-Title>${project.name}</Implementation-Title>
+                            <Implementation-Version>${project.version}</Implementation-Version>
+                            <Bundle-DocURL>${project.url}</Bundle-DocURL>
+                        </instructions>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>ianal-maven-plugin</artifactId>
+                    <version>1.0-alpha-1</version>
+                    <executions>
+                        <execution>
+                            <goals>
+                                <goal>verify-legal-files</goal>
+                            </goals>
+                            <configuration>
+                                <!-- Fail the build if any artifacts are missing legal files -->
+                                <strict>true</strict>
+                            </configuration>
+                        </execution>
+                    </executions>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.rat</groupId>
+                    <artifactId>apache-rat-plugin</artifactId>
+                    <version>0.7</version>
+                    <executions>
+                        <execution>
+                            <phase>verify</phase>
+                            <goals>
+                                <goal>check</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                    <configuration>
+                        <excludeSubProjects>false</excludeSubProjects>
+                        <numUnapprovedLicenses>0</numUnapprovedLicenses>
+                        <excludes>
+                            <!-- General informational files -->
+                            <exclude>README.txt</exclude>
+                            <exclude>CHANGES.txt</exclude>
+                            <!-- generated content -->
+                            <exclude>release.properties</exclude>
+                            <exclude>DEPENDENCIES</exclude>
+                            <exclude>**/.*/**</exclude>
+                            <exclude>**/*.log</exclude>
+                            <exclude>**/maven-eclipse.xml</exclude>
+                            <exclude>**/rat.txt</exclude>
+                            <exclude>**/target/**/*</exclude>
+                            <exclude>**/*.iml</exclude>
+                            <exclude>**/*.ipr</exclude>
+                            <exclude>**/*.iws</exclude>
+                        </excludes>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-checkstyle-plugin</artifactId>
+                    <version>2.2</version>
+                    <dependencies>
+                        <dependency>
+                            <groupId>org.codehaus.plexus</groupId>
+                            <artifactId>plexus-velocity</artifactId>
+                            <version>1.1.3</version>
+                        </dependency>
+                    </dependencies>
+                </plugin>
+                <!-- inherited from apache-10.pom, but need config updates -->
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-release-plugin</artifactId>
+                    <configuration>
+                        <useReleaseProfile>false</useReleaseProfile>
+                        <goals>deploy site site-deploy</goals>
+                        <arguments>-Papache-release</arguments>
+                        <autoVersionSubmodules>true</autoVersionSubmodules>
+                        <preparationGoals>clean install</preparationGoals>
+                    </configuration>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>2.12</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-report-plugin</artifactId>
+                    <version>2.12</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>findbugs-maven-plugin</artifactId>
+                    <version>2.3.2</version>
+                </plugin>
+                <plugin>
+                    <groupId>org.codehaus.mojo</groupId>
+                    <artifactId>buildnumber-maven-plugin</artifactId>
+                    <version>1.0-beta-4</version>
+                </plugin>
+                <plugin>
+                  <groupId>org.apache.maven.plugins</groupId>
+                  <artifactId>maven-source-plugin</artifactId>
+                  <version>2.1.1</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>validate</phase>
+                        <goals>
+                            <goal>enforce</goal>
+                        </goals>
+                        <configuration>
+                            <rules>
+                                <requireMavenVersion>
+                                    <version>[2.2.1,)</version>
+                                </requireMavenVersion>
+                                <requireJavaVersion>
+                                    <version>[1.5,)</version>
+                                </requireJavaVersion>
+                            </rules>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.rat</groupId>
+                <artifactId>apache-rat-plugin</artifactId>
+                <version>0.7</version>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>ianal-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>buildnumber-maven-plugin</artifactId>
+                <configuration>
+                    <getRevisionOnlyOnce>true</getRevisionOnlyOnce>
+                    <revisionOnScmFailure>offline</revisionOnScmFailure>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>validate</phase>
+                        <goals>
+                            <goal>create</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-source-plugin</artifactId>
+              <executions>
+                <execution>
+                  <id>attach-source</id>
+                  <goals>
+                    <goal>jar</goal>
+                  </goals>
+                </execution>
+              </executions>
+            </plugin>
+            <!-- Don't include in all builds for now
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+            </plugin>
+            -->
+        </plugins>
+    </build>
+
+    <modules>
+        <module>bval-core</module>
+        <module>bval-xstream</module>
+        <module>bval-jsr303</module>
+        <module>bundle</module>
+        <module>bval-json</module>
+        <module>bval-guice</module>
+        <module>bval-tck</module>
+        <module>bval-extras</module>
+  </modules>
+
+</project>