2012-02-09 9 views
7

私は、クラスレベルでカスタムアノテーションを使用してクロスフィールド検証(JSR-303)を実装しようとしています。ただし、isValidメソッドは呼び出されません(ただし、initializeメソッド)。クロスフィールド検証のカスタムクラスレベル制約が呼び出されない

私の質問です:isValidメソッドがこのクラスレベルのバリデータに対して呼び出されないのはなぜですか?プロパティレベルで定義することができます!

私はここでは8

AS 7およびWebSphereなどJBoss上でそれを試しているコードと、(動作)JUnitテスト

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()); 
} 

特にプロパティ検索は、クラスレベルの検証制約を呼び出さないこのポストquestion

答えて

8

JSF 2.0から採取しました。 JSF validationから :JSF 2

は、JSR-303制約のある組み込みの統合を提供します。 がアプリケーションでBean検証を使用している場合、JSFは自動的にUIInput値によって参照されるBeanの制約を に使用します。

あなたはそれを手動で呼び出す必要があり、または拡張子<f:validateBean>

+0

ありSeam Faces試すことができます。<豊富:graphValidator値= "#{myBean.ClassWithAnnotationを}" /> RichFacesのからもトリックはありません。私はでそれを行う方法を理解していませんでした。とにかく、標準の説明のおかげでありがとうございました – jonnie119

+0

私はお詫びします、あなたは ''を使ってクロスフィールド検証を行うことができました'' – landal79

+2

@ landal79の場合はどうすれば手動で呼び出すことができますか? – dakait