Rick

Rick
Rick

Tuesday, May 6, 2014

Build your own adventure with Boon.

Validating properties are present with Boon 

RichardHightower edited this page a minute ago · 3 commits

 Pages (34)

Clone this wiki locally

Jackson has configuration in annotations for things like properties that are required. There is nothing wrong with this feature. I have used it myself. But Boon does not do this. Boon is more like MongoDB when it comes to property names/field names. It does not validate the existence of properties by design.
Repeat Boon does not validate existence of properties. 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.

Kafka and Cassandra support, training for AWS EC2 Cassandra 3.0 Training