jscherman jscherman - 2 months ago 9
Java Question

How to validate overriden methods parameters with Hibernate Validator?

Regarding to this doc I understand that if I have my GroupService which implements GroupManager and overrides its methods, then I cannot annotate with validation constraints since Hibernate Validator doesn't allow it (which turns out to be known as the Liskov substitution principle). I mean doing something like

public class GroupService implements GroupManager{

@Override
public List<String> findUsersInGroup(@NotNull String groupName) {
...
}
}


Then a
ConstraintDeclarationException
would be raised, right? So the solution apparently would be to put these constraints on the interface, but in that case:


  1. I could probably not have access to modificate the interface (as this case where
    GroupManager
    belongs to
    Spring Security
    ). How can i do it then?

  2. I think that these validation constraints should not affect the interface since they are part of its implementation, so in case I'd want to have any other service implementation, i shouldn't have to hook it up to these validations. Maybe with this new one I'd want to implement another kind of validation, and
    Hibernate Validator
    forces me to 'dirty up' the interface



What do you think?

Answer

I could probably not have access to modificate interface (as this case where GroupManager belongs to Spring Security). How should i do in this case?

You can use xml configuration since the JSR-303 (Bean Validation) supports it. E.g.

<constraint-mappings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                     xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd"
                     xmlns="http://jboss.org/xml/ns/javax/validation/mapping">
    <default-package>org.springframework.security.provisioning</default-package>
    <bean class="GroupManager" ignore-annotations="true">
        <method name="findUsersInGroup">
            <parameter type="java.lang.String">
                <constraint annotation="javax.validation.constraints.NotNull"/>
            </parameter>
        </method>
    </bean>
</constraint-mappings>

See the xml configuration chapter in the hibernate doc.

I have the thought that these validation constraints should not affect the interface since they are part of its implementation

As the hibernate documentation says

When a method is overridden in sub-types method parameter constraints can only be declared at the base type. The reason for this restriction is that the preconditions to be fulfilled by a type's client must not be strengthened in sub-types (which may not even be known to the base type's client).

The methods preconditions should not be strengthened by sub-types. If you say that your sub-type GroupService does not allow a null parameter you might strengthened the precondition. E.g. a client that uses the GroupManager might not know (and should not know) that it is a GroupService. The GroupManager interface does not make any restrictions to the parameter. So if you do it you break a formerly leagal client code. This violates the Liskov substitution principle.

Sadly the GroupManager javadoc does not restrict the parameter. So a legal implementation must handle every situation.

In general... when I define methodes I apply these rules

  • If a method defines a parameter then it must not be null.
  • If parameters are optional - create overloaded methods and handle optional parameters internally. E.g. by using a null object pattern.

These simple rules help me to create a clear api for clients.

EDIT

i think possible that i have impl "A" and impl "B" (both implementing same interface) where impl "A" has more (and different) validations than "B"

If it is so they do not have the same interface or api. What you see is that both have the same method signature. But two equal method signatures must not share the same api contract. When I talk about an interface I think about the contract and not only the signature. Imagine the following interface:

public class Container {

    /**
     * @return a non-empty collection of elements. 
     */
     public Collection<Element> getElements();
}

In this case legal client code would be

Container container = ....;
Element firstElement = container.getElements().iterator().next();

because the contract says that it returns a non-empty collection.

If we change the javadoc and therefore the post-condition...

 /**
  * @return a collection of elements. 
  */
  public Collection<Element> getElements();

The formerly legal client code would not work anymore.

I just made this example to show you the difference between contract and method signature.

A detailed and very good explanation can be found here.

As GroupManager javadoc doesn't restrict the parameter ,a legal impl must handle every situation'? Is that validating params inside method?

Yes. If the interface does not add any restriction to the parameters the implementation must handle every state, because a client might pass arguments in any state.