A következő címkéjű bejegyzések mutatása: Validate. Összes bejegyzés megjelenítése
A következő címkéjű bejegyzések mutatása: Validate. Összes bejegyzés megjelenítése

2017. október 24., kedd

Implementing inheritance for JSON objects


Our App developers wanted to send slightly different JSON elements in a single list, in order to make the App side implementation far less complicated. In order to make it possible to, I decided to implement inheritance hierarchy for the JSON input and the related java DTO classes.

The other possibility would be to have a JSON object with union of the fields from all Java classes and using @JsonIgnoreProperties(ignoreUnknown = true) for them. In this way you would be able to parse only the relevant field into the given Java class.

Advantages of using hierarchy in JSON related classes:

  • more object oriented implementation
  • you can declare different type of objects in a single JSON list, so far they have the same Java super class
  • automatic REST input validation of the Spring framework still works for the classes. Yo do not need to define checking logic with mixed fields and complicated conditions. 
  • easy to add new types in the future, without effecting the existing ones 

Disadvantages:
  • you need to consider if storing different elements in a single list is a god idea at all
  • complicated structure
  • error prone type definition. It is not possible to define enumeration or any other typed value for the name property of @Type (see below)
  • unit testing of the hierarchy is more complicated
  • difficult to change break the hierarchy structure in case modification is needed later on

I created a common super class for the root of the inheritance and defined the field "actionType" for discriminator.  


@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "actionType")
@JsonSubTypes({
 @Type(value = TriggeredNotificationActionDto.class, name = "NOTIFICATION"),
 @Type(value = TriggeredDeviceActionDto.class, name = "DEVICE")
})
public static class TriggeredActionDto implements Serializable {
 private String actionType;
}

I defined the actual sub-classes (using annotation of Lombok project for getter and setter generation). I also defined validators for the fields. They are used by the Spring framework when the objects are acting as input parameter of a REST call.

@Data
@EqualsAndHashCode(callSuper = true)
public static class TriggeredNotificationActionDto extends TriggeredActionDto {
 public enum NotificationActionType {
  WEBSOCKET, PUSH;
 }

 @JsonProperty("notificationType")
 @NotNull(message = "notificationType must not be blank!")
 private NotificationActionType notificationType;
}



@Data
@EqualsAndHashCode(callSuper = true)
public static class TriggeredDeviceActionDto extends TriggeredActionDto {
 @ApiModelProperty(example = "swagger001")
 @JsonProperty("deviceId")
 @NotNull(message = "Device id must not be blank!")
 private String deviceId;

 @ApiModelProperty(example = "1")
 @JsonProperty("channelId")
 @NotNull(message = "Channel id must not be blank!")
 @Range(min = 1, message = "Channel id must be a positive number")
 private int channelId;

 @ApiModelProperty(example = "1")
 @JsonProperty("value")
 @NotNull(message = "Value must not be blank!")
 private int value;

...
}


In the class, containing the TriggeredActionDto elements, I marked the list as @Valid, in order to force validation of the each elements of the list.


@JsonProperty("tasks")
@Valid
private List<TriggeredActionDto> tasks;



2017. október 5., csütörtök

Custom validator for REST parameter in Spring Boot


Using Spring boot applications, it is very easy to let the Spring framework to do the validation of the REST input parameter. You only need to add the corresponding validation annotations to the properties of the parameter class, and mark the parameter with @Valid annotation in the method header.


@RequestMapping(value = "/register", method = { RequestMethod.POST })
public RestResponse register(HttpServletRequest httpServletRequest, 
       @Valid @RequestBody PushRegistrationRequest registrationRequest) {



In case of your REST input object is validated with the @Valid annotation, most of the time, it is enough to  use default validations, like @NotNull, @Pattern, @Size, etc. Sometimes though you need to do more complex validations, considering multiple properties. To achieve this, you need to write your own annotation, and a corresponding validation class.

The annotation looks like this:


@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = { HasCorrectAppIdValidator.class })
public @interface HasCorrectAppId {
 String message() default "App id is incorrect";

 Class<?>[] groups() default {};

 Class<? extends Payload>[] payload() default {};
}

  As you can see above, in the validatedBy attribute, you can define a validation class. the validator class needs to implement the javax.validation.ConstraintValidator interface.


import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class HasCorrectAppIdValidator implements ConstraintValidator<HasCorrectAppId, PushRegistrationRequest> {

 @Override
 public void initialize(HasCorrectAppId constraintAnnotation) {
  // do nothing
 }

 @Override
 public boolean isValid(PushRegistrationRequest value, ConstraintValidatorContext context) {
  if (IOS.equals(value.getOs()) && value.getAppId().length() != 64) {
   String errorMessage = "length must be 64 for " + ClientOperationSystem.IOS;
   createErrorInContext(context, errorMessage);
   return false;

  } else if (ANDROID.equals(value.getOs()) && value.getAppId().length() < 65) {
   String errorMessage = "length must be greater than 65 for " + ClientOperationSystem.ANDROID;
   createErrorInContext(context, errorMessage);
   return false;
  }
  return true;
 }

 private void createErrorInContext(ConstraintValidatorContext context, String errorMessage) {
  context.disableDefaultConstraintViolation();
  context.buildConstraintViolationWithTemplate(errorMessage)
    .addPropertyNode("appId")
    .addConstraintViolation();
 }
}


That's it. In order to use tell to the framework to use the validator is, to annotate the parameter class with the new annotation.




@HasCorrectAppId
public class PushRegistrationRequest implements Serializable {
    ...
}


2016. szeptember 27., kedd

Different ways to use assertions in Java code



In order to create a clean and save code, I prefer implement as many state checks in my code, as possible or necessary.

If a state check fails, you usually want to throw an exception and stop processing the current business step. Failure in state check is always a sign of an implementation problem. As this kind of problems should be found in your test phase, your end user must not face with this kind of exceptions.

You should consider using assertion in the following situations:
  • Parameter check of the non private methods used by your own components.  
  • As it is stated in the many programming design guides, you always need to check parameters of a public method. It helps to provide a clean API and makes your code safer.
  • Check the state of your object before start to execute a critical operation.
It is also an advantage of assertions, that they can be considered as "active comments" in the code. Using assertions with appropriate error message documents the code, and has a well defined functionality at the same time. Assertions also help to test the API contract by writing Unit test direct against them.

You should carefully consider using assertions in following situations:
  • Checking parameters of private methods. In most cases unnecessary, as the caller of the method can be controlled by you.
  • Check returned objects from other software components, treating them as they would be an input to your component. As the connection and returned objects can be dependent of the environment, it does not necessary indicates a programming error if they are not in an appropriate state. In such situations throw a business exception instead. 

You have following ways to implement assertions

Simple Java check

Implementing the state check with a normal Java code, and throw the appropriate exception, when it fails.

Advantages
  1. You are not bound to a specific framework. 
  2. You can throw any kind of exception, depending of the situation. 
  3. You can implement your own Exception (preferably as a subclass of RuntimeEcxeption), and use it for validations. You can implement the appropriate exception handling for validation problems, make it possible for example to log them separately. You can even inform the developers about problems in production environment.

Disadvantages
  1. You are repeating yourself in the code. 
  2. In a team, all members will implement their own validation, with different, ad hoc Exceptions, and they will tend to forget adding meaningful messages to the exceptions.
  3. You do not have a common way to handle validation failures, as any kind of exception can be thrown. 
  4. Therefore you will soon start to implement your own little framework for validating the state, which is the root of the evil :) 

Java assertion



 public void setRefreshInterval(int interval) {
    // validate input parameter
    assert interval > 0 && interval <= 1000/MAX_REFRESH_RATE : interval;


Advantages
  1. The check is done only in test or development environment, which makes the production code slightly faster.
  2. AssertionError must not be cought, also it does not require additional code to handle it.
Disadvantages
  1. The check is done only in test or development environment, which makes problems, that not found during the acceptance very dangerous. Casing exceptions (if you are lucky) or incorrect business operations (if you are not so lucky)
  2. It is not always obvious to run unit tests with assertions enabled. However in case of Maven's Surefire plugin, assertions are swithced on by default, you need to check this when using other build tools.
    Starting Unit test with assertions enabled from a development tool, like Eclipse is also not convenient.  

Guava Preconditions class

Using the Preconditions class from Guava framework makes it easy to define simple validation rules. It has however not too much methods to be used.

Advantages
  1. Single utility class to validate conditions
Disadvantages
  1. A small set of validation methods

Commons Validate class

Using the Validate class makes it easy to implement validation rules. Its convenient methods are easy to use, and have a very easy to read naming convention.

Advantages
  1. Works with older Java versions as well.
  2. Lots of methods to check null values, collections, ranges, special checks for Strings.
  3. Continuously growing set of methods
Disadvantages
  1. Validation can not be restricted to test evironment
  2. There is no possibility to inform the developer, when problem occures in production.
Sources
http://www.cnblogs.com/rollenholt/p/3655511.html
http://stackoverflow.com/questions/5172948/should-we-always-check-each-parameter-of-method-in-java-for-null-in-the-first-li