Skip to content

Validating properties are present with Boon

Richard Hightower edited this page May 6, 2014 · 3 revisions

Jackson has configuration in annotations for things like properties that are required.

Boon does not. Why? It does not want to. 1st reason Boon has a validation framework, and 2nd the standard Java validation framework exists. 3rd it is hard to have a generic way without a ton of complicated annotations to have validation per use case or view.

But Boon is a set of tools to expose meta-data from Java so you can roll your own framework.

Boon is not a framework. It is a lib. It does not prescribe one way to do things and try to fit every application into a mold. It makes things like querying meta data so you can build your own capabilities, and instead of bending your code to fit the framework, you just use the lib the way that you want.

Frameworks provide training wheels. Some people need training wheels. Some people like to build their own bike. Boon is for the latter.

Here is an example:

Boilerplate Employee class

    public static class Employee {
        String firstName;


        @Required
        String lastName;


        List<String> todo;

        public Employee(String firstName, String lastName, List<String> todo) {
            this.firstName = firstName;
            this.lastName = lastName;
            this.todo = todo;
        }

        @Override
        public String toString() {
            return "Employee{" +
                    "firstName='" + firstName + '\'' +
                    ", lastName='" + lastName + '\'' +
                    ", todo=" + todo +
                    '}';


        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof Employee)) return false;

            Employee employee = (Employee) o;

            if (firstName != null ? !firstName.equals(employee.firstName) : employee.firstName != null) return false;
            if (lastName != null ? !lastName.equals(employee.lastName) : employee.lastName != null) return false;
            if (todo != null ? !todo.equals(employee.todo) : employee.todo != null) return false;

            return true;
        }

        @Override
        public int hashCode() {
            int result = firstName != null ? firstName.hashCode() : 0;
            result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
            result = 31 * result + (todo != null ? todo.hashCode() : 0);
            return result;
        }

        public String getFirstName() {
            return firstName;
        }

        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }

        public String getLastName() {
            return lastName;
        }

        public void setLastName(String lastName) {
            this.lastName = lastName;
        }

        public List<String> getTodo() {
            return todo;
        }

        public void setTodo(List<String> todo) {
            this.todo = todo;
        }
    }

Let's say we wanted to validate that the following JSON parse had the key called todos.

Querying class meta-data

        Map<String, Object> map = mapper.parseMap("{\"firstName\":\"Hovik\",\"lastName\":\"Gambino\"} ");


        //Validate
        boolean hasTodos = map.containsKey("todo");

        if (hasTodos) {
            puts ("It has todo");
        } else {
            puts ("No todos!!!!");
        }

The map is an index overlay which means that it is not fully parsed, but only parses the keys that you query. This is optimized for injecting the map into a Java Object.

So when you are done, you can...


        Employee hovik3WithNoTodos = fromMap(map, Employee.class);

It is flexible. You can bend it. Shape it. There is very little overhead from the above and parsing direct. You could even have complex logic that queries the JSON object and decided for example which message bus it should go to, or validation, or routing, or... whatever. JSON in Boon has a map as an intermediate form. Do stuff with it. Do your own framework stuff.

You can manipulate the map before it gets injected too:

        map.put("todo", list("Read Scala Book", "Learn Boon", "Learn Vertx and Groovy",
                "Buy Rick Lunch", "Buy Rick Beer"));

Now let's say that you wanted to validate all fields that had the annotation Required, well you could do this:

        Iterator<FieldAccess> properties = ClassMeta.classMeta(Employee.class).properties();
        
        map = mapper.parseMap("{\"firstName\":\"Hovik\",\"lastTAAName\":\"Gambino\"} ");


        while (properties.hasNext()) {

            FieldAccess property = properties.next();

            puts (property.name());

            if (property.requiresInjection()) {
                if (map.get(property.name())==null){
                    die("Property was required", property.name());
                }
            }
        }

The above would throw an error until we fixed lastTAAName to be lastName.

Boon also has an object criteria API so you could do validation as follows:

        map = mapper.parseMap("{\"firstName\":\"Hovik\",\"lastName\":\"Gambino\",  \"todo\":[\"Eat Salad\"]} ");


        map.size();

        if (matches(map,
                notNull("firstName"),
                notEmpty("firstName"),
                contains("todo", "Eat Salad"))
                ) {
            puts ("Hovik is cool");
        } else {
            puts ("Hovik eat some damn salad");
        }

You don't have to use pre-caned annotations. You can define your own. Be your own boss.

    /**
     * @author Rick Hightower
     */
    @Target( {ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER} )
    @Retention( RetentionPolicy.RUNTIME )
    public @interface UseCase1 {
    }

...

    public  class Employee {

        String firstName;


        @UseCase1
        String lastName;

...

        ClassMeta<Employee> employeeClassMeta = ClassMeta.classMeta(Employee.class);
        Iterator<FieldAccess> properties = employeeClassMeta.properties();

        map = mapper.parseMap("{\"firstName\":\"Hovik\",\"lastName\":\"Gambino\"} ");


        while (properties.hasNext()) {

            FieldAccess property = properties.next();

            puts (property.name());
            if (property.hasAnnotation("UseCase1")) {
                if (map.get(property.name())==null){
                    die("Property was required", property.name());
                }
            }
        }

Build your own adventure with Boon.

Clone this wiki locally