4

I've got a bean I'd like to do conditional validation on. For this purpose, I've defined a DefaultGroupSequenceProvider<MyObject> which returns the list of groups to validate against.

Now, when validating an object that violates the constraints in more than one group in the sequence, only the first group with failures return it's result. I'd like to get an error on all violations, not just the ones in the first group with failures.

I'm thinking that this doesn't need a code-sample, but if I'm wrong, I'll be happy to provide one.

I followed this http://kh-yiu.blogspot.com/2014/04/conditional-bean-validation-using.html when creating the sequence. We use Spring if that matters.

Just a note, this works, in that it is impossible that an invalid bean will be reported as valid. But if the user has some input that breaks 3 constraints, and I return back 2 failures, the user will get another failure on the last field as soon as the first ones are corrected. Not exactly user-friendly.

Example:

Bean

@GroupSequenceProvider(BeanSequenceProvider.class)
public class MyBean {
    @NotEmpty
    private String name;

    @NotNull
    private MyType type;

    @NotEmpty(groups = Special.class)
    private String lastName;

    // Getters and setters        
}

Enum type

public enum MyType {
    FIRST, SECOND
}

Provider

public class BeanSequenceProvider implements DefaultGroupSequenceProvider<MyBean> {
    @Override
    public List<Class<?>> getValidationGroups(final MyBean object) {
        final List<Class<?>> classes = new ArrayList<>();

        classes.add(MyBean.class);

        if (object != null && object.getType() == MyType.SECOND) {
            classes.add(Special.class);
        }

        return classes;
    }

Group annotation

public interface Special {
}

Test class

public class MyBeanTest {

    private static Validator validator;

    private MyBean objectUnderTest;

    @BeforeClass
    public static void setUpOnce() throws Exception {
        final ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();

        validator = validatorFactory.getValidator();
    }

    @Before
    public void setUp() throws Exception {
        objectUnderTest = new MyBean();

        objectUnderTest.setName("Simen");
        objectUnderTest.setType(MyType.FIRST);
        objectUnderTest.setLastName("Woop");
    }

    @Test
    public void testValid() throws Exception {
        assertThat(validator.validate(objectUnderTest), is(empty()));
    }

    @Test
    public void testMissingName() throws Exception {
        objectUnderTest.setName(null);

        assertThat(validator.validate(objectUnderTest), hasSize(1));
    }

    @Test
    public void testMissingLastName() throws Exception {
        objectUnderTest.setLastName(null);

        assertThat(validator.validate(objectUnderTest), is(empty()));

        objectUnderTest.setType(MyType.SECOND);

        assertThat(validator.validate(objectUnderTest), hasSize(1));

        objectUnderTest.setName(null);

        assertThat(validator.validate(objectUnderTest), hasSize(2));
    }

That very last assert fail, as there's one violation, not 2. As the constraint is violated in the default group, the Special group is not violated.

halfer
  • 19,824
  • 17
  • 99
  • 186
SimenB
  • 6,410
  • 2
  • 23
  • 27
  • I've never worked with this, but maybe you should use something other than the `DefaultGroupSequenceProvider>`? Basically you mean you don't want the error to be returned in sequences, so use something else? – ihsan Nov 28 '14 at 07:34
  • That's what I'm thinking, but my Googling haven't given me any alternatives. Any ideas? – SimenB Nov 28 '14 at 08:21
  • Hmm.. Is there a reason you chose for the `GroupSequenceProvider`? – ihsan Nov 28 '14 at 08:52
  • I have to get the instance I'm validating, as what groups should be validates depends on the value of an enum on that object. My googling led me to the link I provide in the description, which works, as long as all violations are for the same group. – SimenB Nov 28 '14 at 11:56
  • Okay, well, I don't think that I can answer this question. I added it to my favorites and hopefully someone will take it over. Good luck with it! – ihsan Nov 28 '14 at 16:23
  • Actually, you might need to add an example ;-) I don't get what you mean with "when validating an object that violates the rules in more than one sequence". There is only one sequence for a given entity/type, so I don't understand why you are talking about violations in multiple sequences. Best you add an example. – Hardy Dec 01 '14 at 11:04
  • @Hardy example added. Changed the wording to ``when validating an object that violates the constraints in more than one group in the sequence``as well. – SimenB Dec 01 '14 at 22:19

1 Answers1

2

Ok, now I understand your question. The answer is that validation stops if there are one or more violations within a given group. To quote the spec:

Processing a group is defined in Section 4.6, “Validation routine” ; if one of the groups processed in the sequence generates one or more constraint violations, the groups following in the sequence must not be processed. This ensures that a set of constraint is evaluated only if another set of constraint is valid.

See http://beanvalidation.org/1.1/spec/#constraintdeclarationvalidationprocess-groupsequence-groupsequence

In your case there is a violation in the Default group which means the Special group is never validated.

Hardy
  • 18,659
  • 3
  • 49
  • 65
  • 2
    Yes I know, but I would like to get all violations from all groups, not for it to short circuit validation. Do you know of any way I can achieve that? I'm not particularly bound to the current implementaion, that was just the way I got to do the conditional validation I wanted. – SimenB Dec 02 '14 at 15:09