GAE BigTable datatypes and Vaadin Form Properties

As we already discussed, Vaadin provides easy way to edit JAVA POJO object using its com.vaadin.data.util.BeanItem representation, that can be used in Vaadin forms, just out of the box. However we cannot expect that our Java POJO's will have only fields of Strings and some primitives/their wrappers. App Engine Datastore itself provides rich set of basic types - see here for complete reference. As we already show, it was very easy to use Vaadin to bind POJO fields (through Vaadin properties) to UI form elements. It worked great for Strings and primitives/their wrappers, but it will not work for other BigTable data types.
The reason for that is that Vaadin does not know how to handle such classes as com.google.appengine.api.datastore.Email, com.google.appengine.api.datastore.Category or com.google.appengine.api.datastore.GeoPt. However if we look closer into it, we can assume that those types can be quite well represented by java.lang.String with very straightforward rules for conversion. This mean that we can stay with Vaadin TextField UI control and do the conversion from BigTable type to String and vice-versa. Luckilly Vaadin provides a way to do this without resigning from usefulness of automatic binding.
Every Vaadin application (to be specific instance of com.vaadin.server.VaadinServiceSession class) have a field named converterFactory and this field is modifiable by us. It is by default initialized with new instance of com.vaadin.data.util.converter.DefaultConverterFactory, a default implementation that provides converters between data PRESENTATION and data MODEL.
What is PRESENTATION? It is String type in TextField. What is a MODEL? It is an actual data type (I mean Java class) that holds the data. In our BigTable case it can be Email.class, User.class, etc. The com.vaadin.data.util.converter.ConverterFactory is used to provide com.vaadin.data.util.converter.Converter instance capable of doing the conversion from PRESENTATION to MODEL and the other way. As Vaadin can always use the ConverterFactory to lookup the converter it needs and we can substitute converter factory with our own implementation, we can enrich Vaadin data form binding capabilities to great extend. Here is example for Email and String:

package com.appspot.natanedwin.app;

import com.google.appengine.api.datastore.Email;
import com.vaadin.data.util.converter.Converter;
import com.vaadin.data.util.converter.DefaultConverterFactory;
import java.util.Locale;

/**
 * @author BartÅ‚omiej Prokop
 */
public class BigTableConverterFactory extends DefaultConverterFactory {

    @Override
    protected <PRESENTATION, MODEL> Converter<PRESENTATION, MODEL> findConverter(Class<PRESENTATION> presentationType, Class<MODEL> modelType) {
        if (presentationType == String.class && modelType == Email.class) {
            return (Converter<PRESENTATION, MODEL>) new StringToEmail();
        }
        return super.findConverter(presentationType, modelType);
    }

    public static class StringToEmail implements Converter<String, Email> {

        @Override
        public Email convertToModel(String value, Locale locale) throws ConversionException {
            return value == null ? null : new Email(value);
        }

        @Override
        public String convertToPresentation(Email value, Locale locale) throws ConversionException {
            return value == null ? null : value.getEmail();
        }

        @Override
        public Class<Email> getModelType() {
            return Email.class;
        }

        @Override
        public Class<String> getPresentationType() {
            return String.class;
        }
    }
}

Please note that we used here static inner class for coding the actual converter. In real-life for keeping things in respective sizes, we would code each converter in separate .java file. The complete BigTable converters, you will find in book source codes.
The remaining task to do is to set our converter factory in VaadinServiceSession. We shall do this at the beginning of our application session life. The suitable place is init method of our main UI implementation:

package com.appspot.natanedwin.app;

import com.vaadin.ui.UI;

/**
 * @author prokob01
 */
public class AppUI extends UI {

    @Override
    protected void init(VaadinRequest request) {
        ...
        VaadinServiceSession session = getSession();
        session.setConverterFactory(new BigTableConverterFactory());
        ...
    }
    ...
}

Comments

Popular posts from this blog

Dropbear SSH keys and autossh on OpenWRT

Hardening OpenWRT - adding non-root user account

SSH Tunel with OpenWRT