Method-level constraints

It is standard industry practice for good architects to design enterprise applications in sets of tiers with many modules containing public interfaces around opaque implementations. These interfaces are often critical to servicing requests from a client endpoint such as dependent EJB call, web service, or web request. Bean Validation 1.1 allows us to write constraints around these endpoints with a new feature called Method-level constraints.

Method-level validation can only be applied to non-static methods and also constructors. The provider will ignore method-level constraints on static methods.

Method-level constraints require interception technology through the integration with CDI, Spring Framework, or Guice. It works for constructors as well as methods and the validation is implemented as a crosscutting concern.

Bean Validation 1.1 is available for both Java EE 7 Full and Web profile application servers. For earlier Java EE editions, in particular the Web profiles Java EE 5 and 6, the administrator must configure the validator to a CDI container in a proprietary configuration. Note that GlassFish 4 contains the reference implementation Hibernate Validator 5.

Let us start with a stateless session EJB that will stand in a business service that we want to constrain. It is a payment service that takes an account and returns a ticket acknowledging payment and a new account value.

Here is the definition of the PaymentServiceImpl:

package je7hb.beanvalidation.payments;
import javax.ejb.Stateless;
import javax.validation.Valid;
import javax.validation.constraints.*;
import java.math.BigDecimal;
import java.util.Date;

@Stateless
public class PaymentServiceImpl  {
    @NotNull
    @SecureReceipt @Valid
    public Receipt payEntity(
      @NotNull @Valid Account account,
      @NotNull @Size(min = 5, max = 32) String counterparty,
      @NotNull @DecimalMin("0.0") BigDecimal amount)
    {
        String msg;
        if ( counterparty.contains("Bridgetown"))
            msg = "SEC123";
        else
            msg = "Boo! Hoo!";
        Account acct2 = new Account(
                account.getAccount(),
                account.getAmount().subtract(amount));
        return new Receipt(msg,counterparty,acct2, 
                 new Date());
    }
}

The method payEntity() has constraints applied on the arguments for the account, the counterparty, and the monetary value. It also has a custom constraint @SecureReceipt applied on the return value and also a @NotNull constraint. The Bean Validation provider through the interception technology is able to check the input arguments and also the return value. The @Valid constraint informs the Bean Validation provider to recursively apply the constraints to the dependent object. So in this example, we check the input Account value object and also the returned Receipt object instance.

The code snippet for SecureReceipt annotation looks like this:

@Documented
@Constraint(validatedBy = SecureReceiptValidator.class)
@Target({ METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface SecureReceipt {
    String message() default 
"{je7hb.beanvalidation.payment.SecureReceipt.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

The class-level constraint implementation SecureReceiptValidator object looks like this:

public class SecureReceiptValidator
    implements ConstraintValidator<SecureReceipt,Receipt> {
    @Override
    public void initialize(SecureReceipt annotation) { }

    @Override
    public boolean isValid(Receipt value,
                           ConstraintValidatorContext context) {
if ( value == null) return true;
        return value.getMessage().trim().startsWith("SEC");
    }
L}

The isValid() method in SecureReceiptValidator verifies the return object SecureType and has a message that starts with a string SEC. This is a perfect example of how an application can ensure that a business layer has not been compromised and actually provides the correct result to a web client—a RESTful client endpoint or an EJB endpoint.

See the book's source code for the Arquillian unit test class and the value objects Account and Receipt.

Method validation rules

  • Given a method M that is a member of class type T and is passed a value object A, its preconditions may not be strengthened in subtypes of T and A.
  • Given a method M, which is a member of class type T and returns a value object R, its post-conditions may not be weakened in subtypes of T and R.

These rules are imposed by the Bean Validation specification in order to preserve the Barbara Liskov Substitution Principle, which governs the behavior of subtyping. An object is open for extension but closed for modification, and as such if there is a value type of A that extends S then I can replace it with another type B that extends S.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.128.226.121