2012-02-09 12 views
7

Sto tentando di implementare una convalida crossfield (JSR-303) con un'annotazione personalizzata a livello di classe. Tuttavia, il metodo isValid non viene chiamato (ma il metodo di inizializzazione).Vincolo di livello classe personalizzato per convalida Crossfield non chiamato

Quindi la mia domanda è: perché il metodo isValid non viene chiamato per questo validatore di livello di classe? Definirlo a livello di proprietà funziona!

ho provato su JBoss AS 7 e 8. Websphere AS

Ecco il codice e un test JUnit (che funziona)

Test.java

public class Test { 

@org.junit.Test 
public void test() throws ParseException { 
    Person person = new Person(); 
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMDD"); 
    person.setPartyClosingDateFrom(new Date()); 
    person.setPartyClosingDateTo(sdf.parse("20120210")); 
    Set<ConstraintViolation<Person>> violations = Validation.buildDefaultValidatorFactory().getValidator().validate(person); 
    for(ConstraintViolation<Person> violation : violations) { 
     System.out.println("Message:- " + violation.getMessage()); 
    } 
} 
    } 

DateCompare .java

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.Payload; 

@Target({ TYPE}) 
@Retention(RUNTIME) 
@Constraint(validatedBy = DateCompareValidator.class) 
@Documented 
public @interface DateCompare { 

/** First date. */ 
String firstDate(); 

/** Second date. */ 
String secondDate(); 

Class<?>[] constraints() default {}; 

Class<?>[] groups() default {}; 

Class<? extends Payload>[] payload() default {}; 

String message() default "totally wrong, dude!"; 

DateValidator.DateComparisonMode matchMode() default 
    DateValidator.DateComparisonMode.EQUAL; 
} 

DateCompareValidator.java

public class DateCompareValidator implements ConstraintValidator<DateCompare, Object> { 

/** describes the mode the validator should use**/ 
private DateValidator.DateComparisonMode comparisonMode; 

/** The first date field name. */ 
private String firstDateFieldName; 

/** The second date field name. */ 
private String secondDateFieldName; 

/** the message to be used **/ 
private String messageKey = "failure"; 

/** 
* Initialize. 
* 
* This method is used to set the parameters ans is REQUIRED even if you don't use any parameters 
* 
* @param constraintAnnotation the constraint annotation 
*/ 
@Override 
public void initialize(final DateCompare constraintAnnotation) { 
    this.comparisonMode = constraintAnnotation.matchMode(); 
    this.firstDateFieldName = constraintAnnotation.firstDate(); 
    this.secondDateFieldName = constraintAnnotation.secondDate(); 

} 

/** 
* Checks if it is valid. 
* 
* @param target the target 
* @param context the context 
* @return true, if is valid 
*/ 
@Override 
public boolean isValid(final Object target, final ConstraintValidatorContext context) { 
    boolean isValid = true; 

    final Date valueDate1 = DateCompareValidator.getPropertyValue(Date.class, this.firstDateFieldName, target); 
    final Date valueDate2 = DateCompareValidator.getPropertyValue(Date.class, this.secondDateFieldName, target); 
    if (isValid) { 
     isValid = DateValidator.isValid(valueDate1, valueDate2, this.comparisonMode); 
    } else { 
     // at this point comparisonMode does not fit tp the result and we have to 
     // design an error Message 
     final ResourceBundle messageBundle = ResourceBundle.getBundle("resources.messages"); 
     final MessageFormat message = new MessageFormat(messageBundle.getString(this.messageKey)); 
     final Object[] messageArguments = { messageBundle.getString(this.messageKey + "." + this.comparisonMode) }; 

     // replace the default-message with the one we just created 
     context.disableDefaultConstraintViolation(); 
     context.buildConstraintViolationWithTemplate(message.format(messageArguments)).addConstraintViolation(); 
     isValid = false; 
    } 
    return isValid; 
} 


public static <T> T getPropertyValue(final Class<T> requiredType, final String propertyName, final Object instance) { 
    if (requiredType == null) { 
     throw new IllegalArgumentException("Invalid argument. requiredType must NOT be null!"); 
    } 
    if (propertyName == null) { 
     throw new IllegalArgumentException("Invalid argument. PropertyName must NOT be null!"); 
    } 
    if (instance == null) { 
     throw new IllegalArgumentException("Invalid argument. Object instance must NOT be null!"); 
    } 
    T returnValue = null; 
    try { 
     final PropertyDescriptor descriptor = new PropertyDescriptor(propertyName, instance.getClass()); 
     final Method readMethod = descriptor.getReadMethod(); 
     if (readMethod == null) { 
      throw new IllegalStateException("Property '" + propertyName + "' of " + instance.getClass().getName() 
        + " is NOT readable!"); 
     } 
     if (requiredType.isAssignableFrom(readMethod.getReturnType())) { 
      try { 
       final Object propertyValue = readMethod.invoke(instance); 
       returnValue = requiredType.cast(propertyValue); 
      } catch (final Exception e) { 
       e.printStackTrace(); // unable to invoke readMethod 
      } 
     } 
    } catch (final IntrospectionException e) { 
     throw new IllegalArgumentException("Property '" + propertyName + "' is NOT defined in " 
       + instance.getClass().getName() + "!", e); 
    } 
    return returnValue; 
} 

DateValidator.java

public class DateValidator { 

/** 
* The Enum DateComparisonMode. 
* 
* Determins which Type of validation is used 
*/ 
public enum DateComparisonMode { 

    /** the given Date must be BEFORE the referenced Date */ 
    BEFORE, 

    /** the given Date must be BEFORE_OR_EQUAL the referenced Date */ 
    BEFORE_OR_EQUAL, 

    /** the given Date must be EQUAL the referenced Date */ 
    EQUAL, 

    /** the given Date must be AFTER_OR_EQUAL the referenced Date */ 
    AFTER_OR_EQUAL, 

    /** the given Date must be AFTER the referenced Date */ 
    AFTER; 
} 

/** 
* Compare 2 Date Values based on a given Comparison Mode. 
* 
* @param baseDate the base date 
* @param valuationDate the valuation date 
* @param comparisonMode the comparison mode 
* @return true, if is valid 
*/ 
public static boolean isValid(final Date baseDate, final Date valuationDate, final DateComparisonMode comparisonMode) { 
    // Timevalue of both dates will be set to 00:00:0000 
    final Date compValuationDate = DateValidator.convertDate(valuationDate); 
    final Date compBaseDate = DateValidator.convertDate(baseDate); 

    // compare the values 
    final int result = compValuationDate.compareTo(compBaseDate); 

    // match the result to the comparisonMode and return true 
    // if rule is fulfilled 
    switch (result) { 
    case -1: 
     if (comparisonMode == DateComparisonMode.BEFORE) { 
      return true; 
     } 
     if (comparisonMode == DateComparisonMode.BEFORE_OR_EQUAL) { 
      return true; 
     } 

     break; 

    case 0: 
     if (comparisonMode == DateComparisonMode.BEFORE_OR_EQUAL) { 
      return true; 
     } 
     if (comparisonMode == DateComparisonMode.EQUAL) { 
      return true; 
     } 
     if (comparisonMode == DateComparisonMode.AFTER_OR_EQUAL) { 
      return true; 
     } 
     break; 

    case 1: 
     if (comparisonMode == DateComparisonMode.AFTER) { 
      return true; 
     } 
     if (comparisonMode == DateComparisonMode.AFTER_OR_EQUAL) { 
      return true; 
     } 

     break; 
    default: 
     return false; // should not happen.... 
    } 
    return false; 
} 

/** 
* Convert date. 
* 
* sets the time Value of a given Date filed to 00:00:0000 
* 
* @param t the t 
* @return the date 
*/ 
private static Date convertDate(final Date t) { 
    final Calendar calendar = Calendar.getInstance(); 
    calendar.setTime(t); 
    calendar.set(Calendar.HOUR_OF_DAY, 0); 
    calendar.set(Calendar.MINUTE, 0); 
    calendar.set(Calendar.SECOND, 0); 
    calendar.set(Calendar.MILLISECOND, 0); 
    return (calendar.getTime()); 
} 

Soprattutto il recupero di proprietà è stata presa da questo post question

risposta

8

JSF 2.0 non chiama i vincoli di validazione livello di classe . Da JSF validation:

JSF 2 fornisce funzionalità incorporate di integrazione con JSR-303 vincoli. Quando si utilizza nella convalida del bean nell'applicazione, JSF utilizza automaticamente i vincoli per i bean a cui fanno riferimento i valori UIInput.

si deve chiamare manualmente, oppure si può provare Seam Faces che ha un'estensione <f:validateBean>

+0

dal RichFaces fa anche il trucco. non ho capito come farlo con Comunque, grazie per il chiarimento sullo standard – jonnie119

+0

, mi scuso, volevo dire che si poteva usare '' per fare la validazione cross-field come per '' – landal79

+2

@ landal79 come può essere chiamato manualmente ... – dakait

Problemi correlati