You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ja...@apache.org on 2009/08/18 20:24:23 UTC

svn commit: r805524 - in /myfaces/core/trunk: api/ api/src/main/java/javax/faces/component/ api/src/main/java/javax/faces/validator/ impl/src/main/conf/META-INF/ impl/src/main/java/org/apache/myfaces/config/ impl/src/main/resources/javax/faces/

Author: jankeesvanandel
Date: Tue Aug 18 18:24:23 2009
New Revision: 805524

URL: http://svn.apache.org/viewvc?rev=805524&view=rev
Log:
MYFACES-2288 Implement Bean Validation

This is the first part, where the default Bean Validator is added to every input field when present on the build path.
Still working on the validateBean tag, which is a quite difficult one...

Modified:
    myfaces/core/trunk/api/pom.xml
    myfaces/core/trunk/api/src/main/java/javax/faces/component/UIInput.java   (contents, props changed)
    myfaces/core/trunk/api/src/main/java/javax/faces/component/_ComponentUtils.java
    myfaces/core/trunk/api/src/main/java/javax/faces/validator/BeanValidator.java
    myfaces/core/trunk/impl/src/main/conf/META-INF/standard-faces-config-base.xml
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java
    myfaces/core/trunk/impl/src/main/resources/javax/faces/Messages.properties

Modified: myfaces/core/trunk/api/pom.xml
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/pom.xml?rev=805524&r1=805523&r2=805524&view=diff
==============================================================================
--- myfaces/core/trunk/api/pom.xml (original)
+++ myfaces/core/trunk/api/pom.xml Tue Aug 18 18:24:23 2009
@@ -357,7 +357,23 @@
             <version>2.3</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>javax.validation</groupId>
+            <artifactId>validation-api</artifactId>
+            <version>1.0.CR3</version>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
+
+    <repositories>
+        <repository>
+            <id>jboss releases</id>
+            <name>JBoss Releases</name>
+            <url>http://repository.jboss.org/maven2/</url>
+            <layout>default</layout>
+        </repository>
+    </repositories>
+
     <reporting>
         <plugins>
             <plugin>
@@ -409,7 +425,7 @@
                         <ruleset>/rulesets/basic.xml</ruleset>
                         <ruleset>/rulesets/unusedcode.xml</ruleset>
                     </rulesets>
-                    <linkXref>true</linkXref>
+                    <linkXRef>true</linkXRef>
                     <minimumTokens>100</minimumTokens>
                     <targetJdk>1.5</targetJdk>
                     <excludes>

Modified: myfaces/core/trunk/api/src/main/java/javax/faces/component/UIInput.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/java/javax/faces/component/UIInput.java?rev=805524&r1=805523&r2=805524&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/main/java/javax/faces/component/UIInput.java (original)
+++ myfaces/core/trunk/api/src/main/java/javax/faces/component/UIInput.java Tue Aug 18 18:24:23 2009
@@ -18,14 +18,14 @@
  */
 package javax.faces.component;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
 
 import javax.el.ValueExpression;
 import javax.faces.FacesException;
+import javax.faces.application.Application;
 import javax.faces.application.FacesMessage;
+import javax.faces.context.ExternalContext;
 import javax.faces.context.FacesContext;
 import javax.faces.convert.Converter;
 import javax.faces.convert.ConverterException;
@@ -36,10 +36,9 @@
 import javax.faces.event.ValueChangeEvent;
 import javax.faces.event.ValueChangeListener;
 import javax.faces.render.Renderer;
+import javax.faces.validator.BeanValidator;
 import javax.faces.validator.Validator;
-
-import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
-import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
+import java.util.*;
 
 /**
  * UICommand is a base abstraction for components that implement ActionSource.
@@ -68,10 +67,10 @@
     public static final String CONVERSION_MESSAGE_ID = "javax.faces.component.UIInput.CONVERSION";
     public static final String REQUIRED_MESSAGE_ID = "javax.faces.component.UIInput.REQUIRED";
     public static final String UPDATE_MESSAGE_ID = "javax.faces.component.UIInput.UPDATE";
-    public static final String VALIDATE_EMPTY_FIELDS_PARAM_NAME = "javax.faces.VALIDATE_EMPTY_FIELDS";
     private static final String ERROR_HANDLING_EXCEPTION_LIST = "org.apache.myfaces.errorHandling.exceptionList";
 
-    private static final String EMPTY_VALUES_AS_NULL_PARAM_NAME = "javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL";
+    public static final String EMPTY_VALUES_AS_NULL_PARAM_NAME = "javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL";
+    public static final String VALIDATE_EMPTY_FIELDS_PARAM_NAME = "javax.faces.VALIDATE_EMPTY_FIELDS";
 
     private static final Validator[] EMPTY_VALIDATOR_ARRAY = new Validator[0];
 
@@ -327,7 +326,7 @@
         boolean empty = convertedValue == null
                 || (convertedValue instanceof String && ((String) convertedValue).length() == 0);
 
-        if (isRequired() && empty)
+        if (isValid() && isRequired() && empty)
         {
             if (getRequiredMessage() != null)
             {
@@ -346,9 +345,101 @@
 
         if (!empty)
         {
+            //TODO: Jan-Kees: This is not the most elegant solution, but for now it works
+            addDefaultValidators(context);
             _ComponentUtils.callValidators(context, this, convertedValue);
         }
+        else
+        {
+            if (shouldValidateEmptyFields(context))
+            {
+                //TODO: Jan-Kees: This is not the most elegant solution, but for now it works
+                addDefaultValidators(context);
+                _ComponentUtils.callValidators(context, this, convertedValue);
+            }
+        }
+    }
 
+    /**
+     * Add the default Validators to this component.
+     *
+     * @param context The FacesContext.
+     */
+    private void addDefaultValidators(FacesContext context)
+    {
+        Application application = context.getApplication();
+        Map<String, String> defaultValidators = application.getDefaultValidatorInfo();
+        if (defaultValidators != null && defaultValidators.size() != 0)
+        {
+            Set<Map.Entry<String, String>> defaultValidatorInfoSet = defaultValidators.entrySet();
+            for (Map.Entry<String, String> entry : defaultValidatorInfoSet)
+            {
+                String validatorId = entry.getKey();
+                if (shouldAddDefaultValidator(validatorId, context))
+                {
+                    this.addValidator(application.createValidator(validatorId));
+                }
+            }
+        }
+    }
+
+    /**
+     * Determine if the default Validator with the given validatorId should be added to this component.
+     *
+     * @param validatorId The validatorId.
+     * @param context The FacesContext.
+     * @return true if the Validator should be added, false otherwise.
+     */
+    private boolean shouldAddDefaultValidator(String validatorId, FacesContext context)
+    {
+        // Some extra rules are required for Bean Validation.
+        if (validatorId.equals(BeanValidator.VALIDATOR_ID))
+        {
+            if (!BeanValidator.isAvailable)
+            {
+                return false;
+            }
+            ExternalContext externalContext = context.getExternalContext();
+            String disabled = externalContext.getInitParameter(BeanValidator.DISABLE_DEFAULT_BEAN_VALIDATOR_PARAM_NAME);
+            if (disabled != null && disabled.toLowerCase().equals("true"))
+            {
+                return false;
+            }
+        }
+
+        // By default, all default validators should be added
+        return true;
+    }
+
+    private boolean shouldValidateEmptyFields(FacesContext context)
+    {
+        ExternalContext extCtx = context.getExternalContext();
+        String validateEmptyFields = (String) extCtx.getInitParameter(VALIDATE_EMPTY_FIELDS_PARAM_NAME);
+        if (validateEmptyFields == null)
+        {
+            validateEmptyFields = (String) extCtx.getApplicationMap().get(VALIDATE_EMPTY_FIELDS_PARAM_NAME);
+        }
+
+        // null means the same as auto.
+        if (validateEmptyFields == null)
+        {
+            validateEmptyFields = "auto";
+        }
+        else
+        {
+            // The environment variables are case insensitive.
+            validateEmptyFields = validateEmptyFields.toLowerCase();
+        }
+
+        if (validateEmptyFields.equals("auto") && BeanValidator.isAvailable)
+        {
+            return true;
+        }
+        else if (validateEmptyFields.equals("true"))
+        {
+            return true;
+        }
+        return false;
     }
 
     /**
@@ -785,7 +876,7 @@
     {
         return (ValueChangeListener[]) getFacesListeners(ValueChangeListener.class);
     }
-    
+
     enum PropertyKeys
     {
          immediate

Propchange: myfaces/core/trunk/api/src/main/java/javax/faces/component/UIInput.java
------------------------------------------------------------------------------
--- svn:keywords (original)
+++ svn:keywords Tue Aug 18 18:24:23 2009
@@ -1 +0,0 @@
-Date Author Id Revision HeadURL

Modified: myfaces/core/trunk/api/src/main/java/javax/faces/component/_ComponentUtils.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/java/javax/faces/component/_ComponentUtils.java?rev=805524&r1=805523&r2=805524&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/main/java/javax/faces/component/_ComponentUtils.java (original)
+++ myfaces/core/trunk/api/src/main/java/javax/faces/component/_ComponentUtils.java Tue Aug 18 18:24:23 2009
@@ -18,8 +18,6 @@
  */
 package javax.faces.component;
 
-import java.util.Iterator;
-
 import javax.el.ValueExpression;
 import javax.faces.application.FacesMessage;
 import javax.faces.context.FacesContext;
@@ -28,6 +26,8 @@
 import javax.faces.el.ValueBinding;
 import javax.faces.validator.Validator;
 import javax.faces.validator.ValidatorException;
+import java.util.Collection;
+import java.util.Iterator;
 
 /**
  * A collection of static helper methods for locating UIComponents.
@@ -175,6 +175,15 @@
                         facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
                         context.addMessage(input.getClientId(context), facesMessage);
                     }
+                    Collection<FacesMessage> facesMessages = e.getFacesMessages();
+                    if (facesMessages != null)
+                    {
+                        for (FacesMessage message : facesMessages)
+                        {
+                            message.setSeverity(FacesMessage.SEVERITY_ERROR);
+                            context.addMessage(input.getClientId(context), message);
+                        }
+                    }
                 }
             }
         }
@@ -208,6 +217,15 @@
                             facesMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
                             context.addMessage(input.getClientId(context), facesMessage);
                         }
+                        Collection<FacesMessage> facesMessages = ((ValidatorException)cause).getFacesMessages();
+                        if (facesMessages != null)
+                        {
+                            for (FacesMessage message : facesMessages)
+                            {
+                                message.setSeverity(FacesMessage.SEVERITY_ERROR);
+                                context.addMessage(input.getClientId(context), message);
+                            }
+                        }
                     }
                 }
                 else

Modified: myfaces/core/trunk/api/src/main/java/javax/faces/validator/BeanValidator.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/java/javax/faces/validator/BeanValidator.java?rev=805524&r1=805523&r2=805524&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/main/java/javax/faces/validator/BeanValidator.java (original)
+++ myfaces/core/trunk/api/src/main/java/javax/faces/validator/BeanValidator.java Tue Aug 18 18:24:23 2009
@@ -18,81 +18,565 @@
  */
 package javax.faces.validator;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFValidator;
+
+import javax.el.*;
+import javax.faces.FacesException;
+import javax.faces.application.FacesMessage;
 import javax.faces.component.PartialStateHolder;
 import javax.faces.component.UIComponent;
 import javax.faces.context.FacesContext;
+import javax.servlet.ServletContext;
+import javax.validation.ConstraintViolation;
+import javax.validation.MessageInterpolator;
+import javax.validation.Validation;
+import javax.validation.ValidatorFactory;
+import javax.validation.groups.Default;
+import javax.validation.metadata.BeanDescriptor;
+import java.beans.FeatureDescriptor;
+import java.util.*;
 
-public class BeanValidator implements PartialStateHolder, Validator
-{    
-    public static final String DISABLE_DEFAULT_BEAN_VALIDATOR_PARAM_NAME = "javax.faces.validator.DISABLE_DEFAULT_BEAN_VALIDATOR";
-    public static final String EMPTY_VALIDATION_GROUPS_PATTERN = "^[\\W,]*$";
+/**
+ * <p>
+ * <strong>BeanValidator</strong> is a {@link javax.faces.validator.Validator}
+ * that doesn't do any validation itself, but delegates validation logic to
+ * Bean Validation.
+ * </p>
+ *
+ * @author Jan-Kees van Andel
+ * @since 2.0
+ */
+@JSFValidator(
+        name = "f:validateBean",
+        bodyContent = "empty",
+        tagClass = "org.apache.myfaces.taglib.core.ValidateBeanTag")
+@JSFJspProperty(
+        name = "binding",
+        returnType = "javax.faces.validator.BeanValidator",
+        longDesc = "A ValueExpression that evaluates to a BeanValidator.")
+@FacesValidator(value = BeanValidator.VALIDATOR_ID, isDefault = true)
+public class BeanValidator implements Validator, PartialStateHolder
+{
+
+    private static final Log log = LogFactory.getLog(BeanValidator.class);
+
+    /**
+     * Converter ID, as defined by the JSF 2.0 specification.
+     */
+    public static final String VALIDATOR_ID = "javax.faces.Bean";
+
+    /**
+     * The message ID for this Validator in the message bundles.
+     */
     public static final String MESSAGE_ID = "javax.faces.validator.BeanValidator.MESSAGE";
-    public static final String VALIDATION_GROUPS_DELIMITER = ",";
+
+    /**
+     * If this init parameter is present, no Bean Validators should be added to an UIInput by default.
+     * Explicitly adding a BeanValidator to an UIInput is possible though.
+     */
+    public static final String DISABLE_DEFAULT_BEAN_VALIDATOR_PARAM_NAME = "javax.faces.validator.DISABLE_DEFAULT_BEAN_VALIDATOR";
+
+    /**
+     * The key in the ServletContext where the Bean Validation Factory can be found.
+     * In a managed Java EE 6 environment, the container initializes the ValidatorFactory
+     * and stores it in the ServletContext under this key.
+     * If not present, the manually instantiated ValidatorFactory is stored in the ServletContext
+     * under this key for caching purposes.
+     */
     public static final String VALIDATOR_FACTORY_KEY = "javax.faces.validator.beanValidator.ValidatorFactory";
-    public static final String VALIDATOR_ID = "javax.faces.Bean";
-    
+
+    /**
+     * This is used as a separator so multiple validation groups can be specified in one String.
+     */
+    public static final String VALIDATION_GROUPS_DELIMITER = ",";
+
+    /**
+     * This regular expression is used to match for empty validation groups.
+     * Currently, a string containing only whitespace is classified as empty.
+     */
+    public static final String EMPTY_VALIDATION_GROUPS_PATTERN = "^\\s*$";
+
+    private String validationGroups;
+
+    private Class<?>[] validationGroupsArray;
+
+    private boolean isTransient = false;
+
     private boolean _initialStateMarked = false;
-    private boolean _transient;
 
+    /**
+     * This boolean indicates if Bean Validation is present.
+     *
+     * Eager initialization is used for performance. This means Bean Validation binaries
+     * should not be added at runtime after this variable has been set.
+     */
+    public static final boolean isAvailable;
+    static
+    {
+        boolean tmp = false;
+        try
+        {
+            tmp = (Class.forName("javax.validation.Validation") != null);
+        }
+        catch (Throwable t)
+        {
+            log.debug("Error loading class (could be normal)", t);
+            tmp = false;
+        }
+        isAvailable = tmp;
+    }
+
+    /**
+     * This boolean indicates if Unified EL is present.
+     *
+     * Eager initialization is used for performance. This means Unified EL binaries
+     * should not be added at runtime after this variable has been set.
+     */
+    private static final boolean isUnifiedELAvailable;
+    static {
+        boolean tmp = false;
+        try
+        {
+            //TODO: Check this class name when Unified EL for Java EE6 is final.
+            tmp = (Class.forName("javax.el.ValueReference") != null);
+        }
+        catch (Throwable t)
+        {
+            log.debug("Error loading class (could be normal)", t);
+            tmp = false;
+        }
+        isUnifiedELAvailable = tmp;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException
+    {
+        if (context == null) throw new NullPointerException("context");
+        if (component == null) throw new NullPointerException("component");
+
+        if (value == null)
+        {
+            return;
+        }
+
+        // Obtain a reference to the to-be-validated object and the property name.
+        ValueReferenceWrapper valueReferenceWrapper = getValueReference(component, context);
+        if (valueReferenceWrapper == null)
+        {
+            return;
+        }
+        Class<?> valueBaseClass = valueReferenceWrapper.getBase().getClass();
+        String valueProperty = (String) valueReferenceWrapper.getProp();
+        if (valueBaseClass == null || valueProperty == null)
+        {
+            return;
+        }
+
+        // Initialize Bean Validation.
+        ValidatorFactory validatorFactory = createValidatorFactory(context);
+        javax.validation.Validator validator = createValidator(validatorFactory);
+        BeanDescriptor beanDescriptor = validator.getConstraintsForClass(valueBaseClass);
+        if (!beanDescriptor.isBeanConstrained())
+        {
+            return;
+        }
+        Class[] validationGroupsArray = this.validationGroupsArray;
+
+        // Delegate to Bean Validation.
+        Set constraintViolations = validator.validateValue(valueBaseClass, valueProperty, value, validationGroupsArray);
+        if (!constraintViolations.isEmpty())
+        {
+            Set<FacesMessage> messages = new LinkedHashSet<FacesMessage>(constraintViolations.size());
+            for (Object violation: constraintViolations)
+            {
+                ConstraintViolation constraintViolation = (ConstraintViolation) violation;
+                String message = constraintViolation.getMessage();
+                String[] args = new String[]{ message, _MessageUtils.getLabel(context, component) };
+                FacesMessage msg = _MessageUtils.getErrorMessage(context, MESSAGE_ID, args);
+                messages.add(msg);
+            }
+            throw new ValidatorException(messages);
+        }
+    }
+
+    private javax.validation.Validator createValidator(ValidatorFactory validatorFactory)
+    {
+        // Set default validation group when setValidationGroups has not been called.
+        // The null check is there to prevent it from happening twice.
+        if (validationGroupsArray == null)
+        {
+            postSetValidationGroups();
+        }
+
+        return validatorFactory //
+                .usingContext() //
+                .messageInterpolator(FacesMessageInterpolatorHolder.get(validatorFactory)) //
+                .getValidator();
+
+    }
+
+    /**
+     * Get the ValueReference from the ValueExpression.
+     *
+     * @param component The component.
+     * @param context The FacesContext.
+     * @return A ValueReferenceWrapper with the necessary information about the ValueReference.
+     */
+    private ValueReferenceWrapper getValueReference(UIComponent component, FacesContext context)
+    {
+        if (isUnifiedELAvailable)
+        {
+            //TODO: Implement when Unified EL for Java EE6 is available.
+            throw new FacesException("Unified EL for Java EE6 support is not yet implemented");
+        }
+        else
+        {
+            ValueExpression valueExpression = component.getValueExpression("value");
+            ELContext elCtx = context.getELContext();
+            return ValueReferenceResolver.resolve(valueExpression, elCtx);
+        }
+    }
+
+    /**
+     * This method creates ValidatorFactory instances or retrieves them from the container.
+     *
+     * Once created, ValidatorFactory instances are stored in the container under the key
+     * VALIDATOR_FACTORY_KEY for performance.
+     *
+     * @param context The FacesContext.
+     * @return The ValidatorFactory instance.
+     * @throws FacesException if no ValidatorFactory can be obtained because: a) the
+     * container is not a Servlet container or b) because Bean Validation is not available.
+     */
+    private synchronized ValidatorFactory createValidatorFactory(FacesContext context)
+    {
+        Object ctx = context.getExternalContext().getContext();
+        if (ctx instanceof ServletContext)
+        {
+            ServletContext servletCtx = (ServletContext) ctx;
+            Object attr = servletCtx.getAttribute(VALIDATOR_FACTORY_KEY);
+            if (attr != null)
+            {
+                return (ValidatorFactory) attr;
+            }
+            else
+            {
+                if (BeanValidator.isAvailable)
+                {
+                    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+                    servletCtx.setAttribute(VALIDATOR_FACTORY_KEY, attr);
+                    return factory;
+                }
+                else
+                {
+                    throw new FacesException("Bean Validation is not present");
+                }
+            }
+        }
+        else
+        {
+            throw new FacesException("Only Servlet environments are supported for Bean Validation");
+        }
+    }
+
+    /**
+     * Fully initialize the validation groups if needed.
+     * If no validation groups are specified, the Default validation group is used.
+     */
+    private void postSetValidationGroups()
+    {
+        if (this.validationGroups == null || this.validationGroups.matches(EMPTY_VALIDATION_GROUPS_PATTERN))
+        {
+            this.validationGroupsArray = new Class<?>[] { Default.class };
+        }
+        else
+        {
+            String[] classes = this.validationGroups.split(VALIDATION_GROUPS_DELIMITER);
+            List<Class<?>> validationGroupsList = new ArrayList<Class<?>>(classes.length);
+
+            for (String clazz : classes)
+            {
+                clazz = clazz.trim();
+                if (!clazz.equals(""))
+                {
+                    try
+                    {
+                        Class<?> theClass = Class.forName(clazz);
+                        validationGroupsList.add(theClass);
+                    }
+                    catch (ClassNotFoundException e)
+                    {
+                        throw new RuntimeException("Could not load validation group", e);
+                    }
+                }
+            }
+            this.validationGroupsArray = validationGroupsList.toArray(new Class[validationGroupsList.size()]);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object saveState(FacesContext context)
+    {
+        return this.validationGroups;
+    }
+
+    /** {@inheritDoc} */
+    public void restoreState(FacesContext context, Object state)
+    {
+        this.validationGroups = (String) state;
+
+        // Only the String is saved, recalculate the Class[] on state restoration.
+        postSetValidationGroups();
+    }
+
+    /**
+     * Get the Bean Validation validation groups.
+     * @return The validation groups String.
+     */
+    public String getValidationGroups()
+    {
+        return validationGroups;
+    }
+
+    /**
+     * Set the Bean Validation validation groups.
+     * @param validationGroups The validation groups String, separated by
+     *                         {@link BeanValidator#VALIDATION_GROUPS_DELIMITER}.
+     */
+    public void setValidationGroups(String validationGroups)
+    {
+        this.validationGroups = validationGroups;
+        postSetValidationGroups();
+    }
+
+    /** {@inheritDoc} */
+    public boolean isTransient()
+    {
+        return isTransient;
+    }
+
+    /** {@inheritDoc} */
+    public void setTransient(boolean isTransient)
+    {
+        this.isTransient = isTransient;
+    }
+
+    /** {@inheritDoc} */
     @Override
     public void clearInitialState()
     {
         _initialStateMarked = false;
     }
 
+    /** {@inheritDoc} */
     @Override
     public boolean initialStateMarked()
     {
         return _initialStateMarked;
     }
 
+    /** {@inheritDoc} */
     @Override
     public void markInitialState()
     {
         _initialStateMarked = true;
     }
+}
 
-    @Override
-    public boolean isTransient()
+/**
+ * Holder class to prevent NoClassDefFoundError in environments without Bean Validation.
+ *
+ * This is needed, because holder classes are loaded lazily. This means that when it's not
+ * used, it will not be loaded, parsed and initialized. The BeanValidator class is used always,
+ * so the MessageInterpolator references need to be in this separate class.
+ */
+class FacesMessageInterpolatorHolder
+{
+    // Needs to be volatile.
+    private static volatile FacesMessageInterpolator instance;
+
+    /**
+     * Helper method for initializing the FacesMessageInterpolator.
+     *
+     * It uses the "Single Check Idiom" as described in Joshua Bloch's Effective Java 2nd Edition.
+     *
+     * @param validatorFactory Used to obtain the MessageInterpolator.
+     */
+    static MessageInterpolator get(ValidatorFactory validatorFactory)
     {
-        return _transient;
+        FacesMessageInterpolatorHolder.FacesMessageInterpolator ret = instance;
+        if (ret == null)
+        {
+            MessageInterpolator interpolator = validatorFactory.getMessageInterpolator();
+            instance = ret = new FacesMessageInterpolator(interpolator);
+        }
+        return ret;
     }
-    
-    @Override
-    public void setTransient(boolean newTransientValue)
+
+    /**
+     * Standard MessageInterpolator, as described in the JSR-314 spec.
+     */
+    private static class FacesMessageInterpolator implements MessageInterpolator
     {
-        _transient = newTransientValue;
+        private final MessageInterpolator interpolator;
+
+        FacesMessageInterpolator(MessageInterpolator interpolator)
+        {
+            this.interpolator = interpolator;
+        }
+
+        @Override
+        public String interpolate(String s, Context context)
+        {
+            Locale locale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
+            return interpolator.interpolate(s, context, locale);
+        }
+
+        @Override
+        public String interpolate(String s, Context context, Locale locale)
+        {
+            return interpolator.interpolate(s, context, locale);
+        }
     }
+}
 
-    @Override
-    public void restoreState(FacesContext context, Object state)
+/**
+ * This class provides access to the object pointed to by the EL expression.
+ *
+ * It makes the BeanValidator work when Unified EL is not available.
+ */
+class ValueReferenceWrapper
+{
+    private final Object base, prop;
+
+    public ValueReferenceWrapper(Object base, Object prop)
     {
-        // TODO implement here
+        this.base = base;
+        this.prop = prop;
     }
 
-    @Override
-    public Object saveState(FacesContext context)
+    public Object getBase()
+    {
+        return base;
+    }
+
+    public Object getProp()
+    {
+        return prop;
+    }
+}
+
+/**
+ * This class inspects the EL expression and returns a ValueReferenceWrapper
+ * when Unified EL is not available.
+ */
+class ValueReferenceResolver extends ELResolver
+{
+    private final ELResolver resolver;
+
+    /**
+     * This is a simple solution to keep track of the resolved objects,
+     * since ELResolver provides no way to know if the current ELResolver
+     * is the last one in the chain. By assigning (and effectively overwriting)
+     * this field, we know that the value after invoking the chain is always
+     * the last one.
+     *
+     * This solution also deals with nested objects (like: #{myBean.prop.prop.prop}.
+     */
+    private ValueReferenceWrapper lastObject;
+
+    /**
+     * Constructor is only used internally.
+     * @param elResolver
+     */
+    ValueReferenceResolver(ELResolver elResolver)
     {
-        // TODO implement here
-        return null;
+        this.resolver = elResolver;
     }
 
+    /**
+     * This method can be used to extract the ValueReferenceWrapper from the given ValueExpression.
+     *
+     * @param valueExpression The ValueExpression to resolve.
+     * @param elCtx The ELContext, needed to parse and execute the expression.
+     * @return The ValueReferenceWrapper.
+     */
+    public static ValueReferenceWrapper resolve(ValueExpression valueExpression, ELContext elCtx)
+    {
+        ValueReferenceResolver resolver = new ValueReferenceResolver(elCtx.getELResolver());
+        valueExpression.getValue(new MyELContext(elCtx, resolver));
+        return resolver.lastObject;
+    }
+
+    /**
+     * This method is the only one that matters. It keeps track of the objects in the EL expression.
+     *
+     * It creates a new ValueReferenceWrapper and assigns it to lastObject.
+     *
+     * @param context The ELContext.
+     * @param base The base object, may be null.
+     * @param property The property, may be null.
+     * @return The resolved value
+     */
     @Override
-    public void validate(FacesContext context, UIComponent component,
-            Object value) throws ValidatorException
+    public Object getValue(ELContext context, Object base, Object property)
     {
-        // TODO implement here
+        lastObject = new ValueReferenceWrapper(base, property);
+        return resolver.getValue(context, base, property);
     }
-    
-    public String getValidationGroups()
+
+    // ############################ Standard delegating implementations ############################
+    public Class<?> getType(ELContext ctx, Object base, Object property){return resolver.getType(ctx, base, property);}
+    public void setValue(ELContext ctx, Object base, Object property, Object value){resolver.setValue(ctx, base, property, value);}
+    public boolean isReadOnly(ELContext ctx, Object base, Object property){return resolver.isReadOnly(ctx, base, property);}
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext ctx, Object base){return resolver.getFeatureDescriptors(ctx, base);}
+    public Class<?> getCommonPropertyType(ELContext ctx, Object base){return resolver.getCommonPropertyType(ctx, base);}
+
+}
+
+/**
+ * This ELContext is used to hook into the EL handling, by decorating the
+ * ELResolver chain with a custom ELResolver.
+ */
+class MyELContext extends ELContext
+{
+    private final ELContext ctx;
+    private final ELResolver interceptingResolver;
+
+    /**
+     * Only used by ValueExpressionResolver.
+     *
+     * @param elContext The standard ELContext. All method calls, except getELResolver, are delegated to it.
+     * @param interceptingResolver The ELResolver to be returned by getELResolver.
+     */
+    MyELContext(ELContext elContext, ELResolver interceptingResolver)
     {
-        //TODO implement here
-        return null;
+        this.ctx = elContext;
+        this.interceptingResolver = interceptingResolver;
     }
-    
-    public void setValidationGroups(String validationGroups)
+
+    /**
+     * This is the important one, it returns the passed ELResolver.
+     * @return The ELResolver passed into the constructor.
+     */
+    @Override
+    public ELResolver getELResolver()
     {
-        //TODO implement here
+        return interceptingResolver;
     }
 
+    // ############################ Standard delegating implementations ############################
+    public FunctionMapper getFunctionMapper(){return ctx.getFunctionMapper();}
+    public VariableMapper getVariableMapper(){return ctx.getVariableMapper();}
+    public void setPropertyResolved(boolean resolved){ctx.setPropertyResolved(resolved);}
+    public boolean isPropertyResolved(){return ctx.isPropertyResolved();}
+    public void putContext(Class key, Object contextObject){ctx.putContext(key, contextObject);}
+    public Object getContext(Class key){return ctx.getContext(key);}
+    public Locale getLocale(){return ctx.getLocale();}
+    public void setLocale(Locale locale){ctx.setLocale(locale);}
 }

Modified: myfaces/core/trunk/impl/src/main/conf/META-INF/standard-faces-config-base.xml
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/conf/META-INF/standard-faces-config-base.xml?rev=805524&r1=805523&r2=805524&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/conf/META-INF/standard-faces-config-base.xml (original)
+++ myfaces/core/trunk/impl/src/main/conf/META-INF/standard-faces-config-base.xml Tue Aug 18 18:24:23 2009
@@ -45,6 +45,9 @@
          <supported-locale>ru</supported-locale>
          <supported-locale>zh_CN</supported-locale>
       </locale-config>
+      <default-validators>
+         <validator-id>javax.faces.Bean</validator-id>
+      </default-validators>
    </application>
    <factory>
       <application-factory>org.apache.myfaces.application.ApplicationFactoryImpl</application-factory>

Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java?rev=805524&r1=805523&r2=805524&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/FacesConfigurator.java Tue Aug 18 18:24:23 2009
@@ -18,55 +18,6 @@
  */
 package org.apache.myfaces.config;
 
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.JarURLConnection;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.TreeMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import javax.el.ELResolver;
-import javax.faces.FacesException;
-import javax.faces.FactoryFinder;
-import javax.faces.application.Application;
-import javax.faces.application.ApplicationFactory;
-import javax.faces.application.NavigationHandler;
-import javax.faces.application.ResourceHandler;
-import javax.faces.application.StateManager;
-import javax.faces.application.ViewHandler;
-import javax.faces.context.ExternalContext;
-import javax.faces.el.PropertyResolver;
-import javax.faces.el.VariableResolver;
-import javax.faces.event.ActionListener;
-import javax.faces.event.PhaseListener;
-import javax.faces.event.SystemEvent;
-import javax.faces.lifecycle.Lifecycle;
-import javax.faces.lifecycle.LifecycleFactory;
-import javax.faces.render.RenderKit;
-import javax.faces.render.RenderKitFactory;
-import javax.faces.webapp.FacesServlet;
-
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.myfaces.application.ApplicationFactoryImpl;
@@ -80,12 +31,8 @@
 import org.apache.myfaces.config.element.Renderer;
 import org.apache.myfaces.config.impl.digester.DigesterFacesConfigDispenserImpl;
 import org.apache.myfaces.config.impl.digester.DigesterFacesConfigUnmarshallerImpl;
-import org.apache.myfaces.config.impl.digester.elements.ConfigOthersSlot;
-import org.apache.myfaces.config.impl.digester.elements.FacesConfig;
-import org.apache.myfaces.config.impl.digester.elements.FacesConfigNameSlot;
-import org.apache.myfaces.config.impl.digester.elements.OrderSlot;
+import org.apache.myfaces.config.impl.digester.elements.*;
 import org.apache.myfaces.config.impl.digester.elements.ResourceBundle;
-import org.apache.myfaces.config.impl.digester.elements.SystemEventListener;
 import org.apache.myfaces.context.ExceptionHandlerFactoryImpl;
 import org.apache.myfaces.context.ExternalContextFactoryImpl;
 import org.apache.myfaces.context.FacesContextFactoryImpl;
@@ -106,6 +53,33 @@
 import org.apache.myfaces.view.facelets.util.Classpath;
 import org.xml.sax.SAXException;
 
+import javax.el.ELResolver;
+import javax.faces.FacesException;
+import javax.faces.FactoryFinder;
+import javax.faces.application.Application;
+import javax.faces.application.*;
+import javax.faces.context.ExternalContext;
+import javax.faces.el.PropertyResolver;
+import javax.faces.el.VariableResolver;
+import javax.faces.event.ActionListener;
+import javax.faces.event.PhaseListener;
+import javax.faces.event.SystemEvent;
+import javax.faces.lifecycle.Lifecycle;
+import javax.faces.lifecycle.LifecycleFactory;
+import javax.faces.render.RenderKit;
+import javax.faces.render.RenderKitFactory;
+import javax.faces.webapp.FacesServlet;
+import java.io.*;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 /**
  * Configures everything for a given context. The FacesConfigurator is independent of the concrete implementations that
  * lie behind FacesConfigUnmarshaller and FacesConfigDispenser.
@@ -1664,7 +1638,12 @@
         {
             application.addValidator(validatorId, dispenser.getValidatorClass(validatorId));
         }
-        
+
+        for (String validatorId : dispenser.getDefaultValidatorIds())
+        {
+            application.addDefaultValidatorId(validatorId);
+        }
+
         for (Behavior behavior : dispenser.getBehaviors()) {
             application.addBehavior(behavior.getBehaviorId(), behavior.getBehaviorClass());
         }

Modified: myfaces/core/trunk/impl/src/main/resources/javax/faces/Messages.properties
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/resources/javax/faces/Messages.properties?rev=805524&r1=805523&r2=805524&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/resources/javax/faces/Messages.properties (original)
+++ myfaces/core/trunk/impl/src/main/resources/javax/faces/Messages.properties Tue Aug 18 18:24:23 2009
@@ -95,6 +95,8 @@
 
 javax.faces.validator.RegexValidator.NOT_MATCHED = {1}: Validation Error: Value not according to pattern ''{0}''
 
+javax.faces.validator.BeanValidator.MESSAGE = {1}: {0}
+
 # myfaces specific messages
 org.apache.myfaces.renderkit.html.HtmlMessagesRenderer.IN_FIELD = \u0020in {0}
 org.apache.myfaces.Email.INVALID = Validation Error