2017-05-16 3 views
0

J'essaye d'implémenter des règles d'activation pour OVal suite à leur documentation mais semble rencontrer des problèmes avec lui trouver la variable que j'utilise pour la comparaison. Malheureusement, à part la petite section dans leur documentation, il n'y a pas beaucoup de choses en ligne sur le sujet.OVal règles d'activation pour les contraintes ne fonctionne pas

L'autre partie du problème que j'essaye de résoudre est également d'obtenir ceci fonctionnant pour la validation de constructeur en utilisant l'annotation @Guarded. Cela fonctionne bien sans les règles de contrainte comme décrit dans ma réponse à this question, mais pas lorsque j'ajoute des règles d'activation soit JavaScript ou Groovy.

3.4. Déclarant règles d'activation des contraintes

public class BusinessObject 
{ 
    private String fieldA; 

    @NotNull(when = "groovy:_this.fieldA != null") 
    private String fieldB; 
} 

J'ai essayé à la fois JS et groovy et essayé avec et sans le _this. En supprimant cela, on obtient: ReferenceError: "someString" is not defined Donc je suppose que la façon dont ils sont listés dans les docs est correcte mais il me manque quelque chose.

Code pour la validation sur le terrain:

public class BusinessObject { 
    private String fieldA; 

    //@NotNull(when = "groovy:_this.fieldA != null") //works for public & private 
    @NotNull(when = "javascript:_this.fieldA != null") //only works when fieldA is public 
    private String fieldB; 

    public BusinessObject(){} 

    public BusinessObject(String fieldA, String fieldB) { 
     this.fieldA = fieldA; 
     this.fieldB = fieldB; 
    } 
} 
code

pour la validation du constructeur:

@Guarded 
public class BusinessObjectConstructorValidation { 
    private String fieldA; 
    private String fieldB; 

    public BusinessObjectConstructorValidation(
      String fieldA, 
      @NotNull(when = "groovy:_this.fieldA != null") String fieldB) { 

     this.fieldA = fieldA; 
     this.fieldB = fieldB; 
    } 
} 

Comment je teste l'objet:

public class BusinessObjectTest { 

    @Test 
    public void fieldANullFieldBNotValidatedNoViolations() { 
     BusinessObject businessObject = new BusinessObject(null, null); 
     Validator validator = new Validator(); 
     validator.validate(businessObject); 
    } 

    //This test will fail if the fields are private and using javascript 
    //If it's public or using groovy it passes 
    @Test 
    public void fieldANotNullFieldBValidatedViolationsSizeIsOne() { 
     BusinessObject businessObject = new BusinessObject("A", null); 
     Validator validator = new Validator(); 
     List<ConstraintViolation> errors = validator.validate(businessObject); 
     System.out.println(errors.size()); 
     assertThat(errors.size(), is(1)); 
    } 

    @Test 
    public void fieldANullFieldBNotNullNoViolations() { 
     BusinessObject businessObject = new BusinessObject(null, "B"); 
     Validator validator = new Validator(); 
     validator.validate(businessObject); 
    } 
} 

Je ne sais pas pourquoi la La version JavaScript se comporte différemment de la version groovy, j'ai essayé de changer toutes les combinaisons auxquelles je pouvais penser, y compris: _this.fieldA, __this.fieldA, window.fieldA, fieldA et __fieldA

MISE À JOUR Le JavaScript semble fonctionner pour les champs privés aussi longtemps qu'il a un getter public.

Répondre

0

J'ai résolu le problème en faisant ce qui suit et en passant à groovy au lieu de JavaScript comme suggéré par le developer.

Le moteur JavaScript Rhino ne peut pas accéder directement aux champs privés. Par exemple. quelque chose comme "javascript: _this.myPrivateField! = null & & _this.myPrivateField.length> 10" retournera toujours faux, peu importe la valeur du champ privé. Rhino ne déclenche apparemment pas d'exception s'il ne peut pas accéder au champ privé.

Je vous recommande d'utiliser groovy au lieu de javascript pour activer les contraintes. Pour la plupart des runtimes de script (y compris JavaScript), l'instruction _this.someVariable résultera en l'invocation du getter _this.getSomeVariable() et non en un accès direct à un champ privé portant le même nom.

Dans la première ligne de mon exemple de code du (checkInvariants = false) est nécessaire si l'objet que vous validation a des méthodes de lecture publique sinon il en résultera une StackOverflowError

Malheureusement ajouter la validation dans le constructeur que je posté dans ma question ne fonctionne pas. Pour contourner ce problème, j'ai dû ajouter la validation au champ et ajouter l'annotation @PostValidateThis au constructeur.

Exemple Pojo utilisant la validation après l'appel du constructeur.

@Guarded(checkInvariants = false)// removing this results in StackOverflowError 
public class User { 
    private final String firstName; 
    private final String lastName; 
    @NotNull(when = "groovy:_this.lastName != null") 
    private final Integer age; 

    @PostValidateThis 
    public User(String firstName, String lastName, Integer age) { 
     this.firstName = firstName; 
     this.lastName = lastName; 
     this.age = age; 
    } 

    public String getFirstName() { 
     return firstName; 
    } 

    public String getLastName() { 
     return lastName; 
    } 

    public Integer getAge() { 
     return age; 
    } 
} 

test unitaire de base pour le POJO ci-dessus.

public class UserTest { 
    @Test 
    public void userValidParamsNoException() throws Exception { 
     User user = new User("foo","bar",123); 
     assertThat(user, is(not(nullValue()))); 
     assertThat(user.getFirstName(), is("foo")); 
     assertThat(user.getLastName(), is("bar")); 
     assertThat(user.getAge(), is(123)); 
    } 

    @Test 
    public void userLastNameNullNoException() throws Exception { 
     User user = new User("foo",null, null); 
     assertThat(user, is(not(nullValue()))); 
     assertThat(user.getFirstName(), is("foo")); 
    } 

    @Test(expected = ConstraintsViolatedException.class) 
    public void userLastNameNotNullAgeNullThrowsException() throws Exception { 
     User user = new User("foo","bar", null); 
    } 
}