lunes, 19 de octubre de 2015

Una herramienta práctica para mostrar código en el Blog


Muchas veces tenemos problemas para mostrar código en nuestro blog y nos auxiliamos de plugins que ejecutan JavaScript, pero esto no certifica que se mostrará correctamente, ya que el cliente puede evitar que corra o la PC le tome mucho en procesarlo dependiendo de la máquina (Dispositivos móviles muy lentos) y sea desagradable ver dicho apartado del blog.

Pues una solución es crear a mano el código usando HTML y CSS para decorar y hacer más amigable a los dispositivos lentos nuestro blog, pero eso es muy trabajoso, pero podemos auxiliarnos de una herramienta en linea que hace eso por nosotros y con unas cuantas lineas de JavaScript le da la apariencia, puesto carga el CSS igual.

Aquí tenemos al SyntaxHighlightGenerator-v3.0 con esta herramientas convertimos nuestro código en puro HTML que hace la sintaxis y adicionando unas lineas al cuerpo de nuestro blog logramos ese fabuloso y simple contexto de código indentado sin necesidad de sacrificar a los dispositivos lentos.



Vaadin, Spring Boot, GORM y GRADLE para soluciones interesantes

Hoy día podemos usar GRAILS desde cualquier sistema de automático de construcción (GRADLE, MAVEN, IVY), pero aveces queremos realizar pequeños proyectos con solo ciertas herramientas y con un desarrollo web rápido debido al entorno y necesidad generados por el proyecto. Por que perder a un gran ORM como GORM?, Tengo que usar GRAILS para usar a GORM?

Para los amantes de Micro Frameworks Spring Boot es una gran opción ante otros (Spark Java, NodeJS) si lo que se quiere es mantener un lenguaje alto con altas herramientas para soluciones sencillas, pero que necesitan un poco (Incluso son viables si el proyecto es de tamaño medio) del elemento de programación.

Para el ejemplo usé el Plugin de Grails para las Grid y Tables Lazy que anteriormente mostré como usar y crear para enseñar que sí funciona! :V

Usando nuestro IDE favorito creamos un proyecto GRADLE limpio y procedemos a adicionar los siguientes repositorios a nuestra configuración.
  • maven { url "https://repo.spring.io/libs-release" } // Spring repo
  • maven { url "http://maven.vaadin.com/vaadin-addons" } // Vaadin Add-ON
  • maven { url "http://dl.bintray.com/johndevs/maven" } // Vaadin on Gradle
Nota: Se deben poner en

buildscript {
    repositories {
        // Aqui
    }
}
repositories {
    // Aqui
}

Una vez con todos los repositorios están en su lugar procedemos a usar los componentes para crear nuestro primer proyecto.

Declaramos las siguientes variables para controlar las versiones (intentamos usar las últimas, no confundan las de GORM [4.0.1.RELEASE que es vieja]):

def vaadinVersion = '7.5.7'
def vaadinSpringVersion = "1.0.0"
def springBootVersion = "1.2.7.RELEASE"
def gormGrailsVersion = "1.1.0.RELEASE"

Luego activamos los plugins necesarios:

apply plugin: 'groovy'
apply plugin: 'java'
apply plugin: 'spring-boot'
apply plugin: 'war'
apply plugin: fi.jasoft.plugin.GradleVaadinGroovyPlugin

Adicionar al ClassPath lo siguiente para configurar el proyecto.

classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.7.RELEASE")
classpath "fi.jasoft.plugin:gradle-vaadin-plugin:0.10.2"

Adicionamos al final lo siguiente para determinar la versión de VAADIN y evitar problemas con Hibernate con una librería que VAADIN usa en una versión diferente.

jar {
    from sourceSets.main.allJava
}

configurations {
    //noinspection GroovyAssignabilityCheck
    'vaadin-client' {
        resolutionStrategy.force "javax.validation:validation-api:1.0.0.GA"
        exclude module: 'spring-boot-starter-web'
        exclude module: 'spring-boot-vaadin'
    }
}

vaadin {
    //noinspection GroovyAssignabilityCheck
    version "$vaadinVersion"
    widgetset 'AppWidgetSet'
}

Por último adicionamos las librerías a usar.

    compile 'org.codehaus.groovy:groovy-all:2.4.5'

    // Activo a Spring Boot
    compile "org.springframework.boot:spring-boot-starter:$springBootVersion"
    // Con esto activo el web, pero es posible compilar el JAR.
    compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
    compile "org.springframework.boot:spring-boot-parent:$springBootVersion"
    providedRuntime "org.springframework.boot:spring-boot-starter-tomcat:$springBootVersion"

    // Activo a GORM
    compile("org.grails:gorm-hibernate4-spring-boot:$gormGrailsVersion") {
        exclude module: 'groovy-all'
    }
    // Permite que GORM y Vaadin Existan juntos
    compile 'javax.validation:validation-api:1.0.0.GA'
    // Permite el uso de la DB y es obligatorio para que funcione la DB con o sin GORM
    compile 'commons-dbcp:commons-dbcp:1.4'

    // Activo el vaadin Official
    compile "com.vaadin:vaadin-spring:$vaadinSpringVersion"
    compile "com.vaadin:vaadin-spring-boot:$vaadinSpringVersion"
    compile "com.vaadin:vaadin-spring-boot-starter:$vaadinSpringVersion"

    // DB Driver
    compile 'com.h2database:h2:1.4.190'

    // Vaadin Add-On
    compile("org.vaadin.addons.lazyquerycontainer:vaadin-lazyquerycontainer:7.4.0.1") {
        // Evita que me descargue dependencias no necesarias para mi caso.
        // Trae consigo una versión de VAADIN vieja.
        transitive = false
    }

    testCompile group: 'junit', name: 'junit', version: '4.12'

Una vez completado esto podemos refrescar el GRADLE y esperamos que las dependencias descarguen.

Ya con todo esto incluido en nuestro proyecto podemos crear nuestras primeras UI con vaadin, al igual que crear nuestros servicios, domains y utilerías.

Nota:

  • Usar un installApp permite correr la aplicación sin necesidad de un server, puesto usa el mismo tomcat que se especificó. Esto nos permite mostrar la APP a clientes sin necesidad de tener una PC configurada.


Todo esto podrán verlo directamente en el código fuente anexo.

Más información aquí
Example build.gradle
Source GIT Example TestGrailsVaadinSpringBoot

sábado, 17 de octubre de 2015

Implementación de org.vaadin.addons.lazyquerycontainer 7.4.0.1


Otra vez contenedores vagos en Grails y Vaadin! Yeah!

Usando el plugin que ahora forma parte del Framework Vaadin (Vaadin Plugin Lazy Query Container) ahora podemos crear esas tablas con cientos de miles de filas que necesitan ser paginadas de una forma relativamente fácil y práctica.

Debido a que ese plugin usa el estándar JAVA para poder trabajar, se nos dificulta un poco usar en Grails debido a que no vemos de frente la DB, sino que lo hacemos mediante GORM y nos pone a pensar un poco en como hacemos la implementación.

Para mi caso particular anexo el source directo, pero pueden verlo igual en el repositorio del TestLazyContainerVaadinGrails con un ejemplo de Demo2 escrito en la parte Groovy del proyecto. Cabe destacar que la implementación esta escrita puramente en JAVA porque la compilación dinámica presenta problemas para este tipo de abstracción. Empecemos!

Lo primero es entender como funciona el nuevo plugin y si ya han usado el anterior más o menos tendrán idea, pero explico.

Se tiene el contenedor (Container) que es el recipiente de la data y este a su vez esta unido a un creador de consultas (QueryFactory) y así mismo tenemos la consulta en sí (Query) que es creada por el creador de consultas.

Expliquemos de forma simple como funcionan estos 3 componentes:

  • El contenedor (Container): hace lo que dice su nombre y es el que crea el enlace de la tabla, la data y el servidor. Este a su vez maneja las columnas que se mostrarán.
  • El creador de consultas (QueryFactory): es el encargado de manejar como se va a machear la data de forma que coincida con las columna del contenedor (Container)
  • La consulta (Query): es la implementación misma de donde se buscan los datos es el conector real entre servidor y cliente.

Una vez claros con los conceptos entonces procedemos a crear nuestras implementaciones, pero por motivos de practicidad y de mantener un código más simple y rápido en el desarrollo de las UI con Grails y Vaadin yo mismo creé una abstracción más compleja, pero simple de implementar con tal de facilitar la creación (que conste que aún se puede usar Reflection para crear de forma dinámica las columnas y quizás mejorar la forma en que la data en memoria no se pierda para reducir aún más las consultas a la DB, pero ese es su problema, puesto no les puedo hacer la vida tán fácil sin que paguen :P jijiji) y ahora verán un ejemplo de ella.

Que conste que son libres de implementar directamente las interfaces sin la abstracción independientemente a cada clase en particular, pero notarán que ahorro muchas lineas de código.

Primero vamos a crear una interfaz que nos permita tener las consultas separadas del código de los contenedores, puesto ciertos elementos cambiantes en la UI modifican la consulta y sería tedioso llevarlos a clases independientes que en sí no deben saber cómo les llega la DATA, sino que le llegue.

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.util.List;
/**
 *  Created by aluis on 10/8/15.
 */
public interface LazyQuery<BEANTYPE> {
    /**
     * Get Size by a Query
     */
    int getSize();
    /**
     * Get items to view on the page using the same model of the container.
     */
    List<BEANTYPE> getItemsIds(int startIndex, int numberOfIds);
    /**
     * Get Size for manual filter.
     */
    int getFilteredSize();
    /**
     * Get items to view on the page for manual filter using the same model of the container.
     */
    List<BEANTYPE> getFilteredItemsIds(int startIndex, int numberOfIds);
}
import java.util.List;

/**
 *  Created by aluis on 10/8/15.
 */
public interface LazyQuery<BEANTYPE> {

    /**
     * Get Size by a Query
     */
    int getSize();

    /**
     * Get items to view on the page using the same model of the container.
     */
    List<BEANTYPE> getItemsIds(int startIndex, int numberOfIds);

    /**
     * Get Size for manual filter.
     */
    int getFilteredSize();

    /**
     * Get items to view on the page for manual filter using the same model of the container.
     */
    List<BEANTYPE> getFilteredItemsIds(int startIndex, int numberOfIds);
}

Una vez creada la interfaz procedemos a crear nuestra consulta (Query)

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import com.vaadin.data.Item;
import org.vaadin.addons.lazyquerycontainer.Query;
import java.util.ArrayList;
import java.util.List;
/**
 *  Created by aluis on 10/8/15.
 */
public abstract class BaseQuery<BEANTYPE> implements Query {
    protected BaseQueryFactory<BEANTYPE> baseQueryFactory;
    public boolean filtered = false;
    protected BaseQuery(BaseQueryFactory<BEANTYPE> baseQueryFactory) {
        this.baseQueryFactory = baseQueryFactory;
    }
    @Override
    public int size() {
        if (filtered) {
            return baseQueryFactory.getLazyQuery().getFilteredSize();
        }
        return baseQueryFactory.getLazyQuery().getSize();
    }
    @SuppressWarnings("Convert2streamapi")
    @Override
    public List<Item> loadItems(int startIndex, int numberOfIds) {
        List<Item> items = new ArrayList<>();
        List<BEANTYPE> allData;
        if (filtered) {
            allData = baseQueryFactory.getLazyQuery().getFilteredItemsIds(startIndex, numberOfIds);
        } else {
            allData = baseQueryFactory.getLazyQuery().getItemsIds(startIndex, numberOfIds);
        }
        for (BEANTYPE data : allData) {
            items.add(baseQueryFactory.constructItem(data));
        }
        return items;
    }
    @Override
    public void saveItems(List<Item> list, List<Item> list1, List<Item> list2) {
        throw new UnsupportedOperationException();
    }
    @Override
    public boolean deleteAllItems() {
        throw new UnsupportedOperationException();
    }
    @Override
    public Item constructItem() {
        return baseQueryFactory.constructItem();
    }
}
import com.vaadin.data.Item;
import org.vaadin.addons.lazyquerycontainer.Query;

import java.util.ArrayList;
import java.util.List;

/**
 *  Created by aluis on 10/8/15.
 */
public abstract class BaseQuery<BEANTYPE> implements Query {

    protected BaseQueryFactory<BEANTYPE> baseQueryFactory;
    public boolean filtered = false;

    protected BaseQuery(BaseQueryFactory<BEANTYPE> baseQueryFactory) {
        this.baseQueryFactory = baseQueryFactory;
    }

    @Override
    public int size() {
        if (filtered) {
            return baseQueryFactory.getLazyQuery().getFilteredSize();
        }
        return baseQueryFactory.getLazyQuery().getSize();
    }

    @SuppressWarnings("Convert2streamapi")
    @Override
    public List<Item> loadItems(int startIndex, int numberOfIds) {
        List<Item> items = new ArrayList<>();
        List<BEANTYPE> allData;
        if (filtered) {
            allData = baseQueryFactory.getLazyQuery().getFilteredItemsIds(startIndex, numberOfIds);
        } else {
            allData = baseQueryFactory.getLazyQuery().getItemsIds(startIndex, numberOfIds);
        }
        for (BEANTYPE data : allData) {
            items.add(baseQueryFactory.constructItem(data));
        }
        return items;
    }

    @Override
    public void saveItems(List<Item> list, List<Item> list1, List<Item> list2) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean deleteAllItems() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Item constructItem() {
        return baseQueryFactory.constructItem();
    }
}

Ya con nuestra consulta (Query) hecha podemos crear nuestro creador de consultas (QueryFactory). En este tenemos 2 atributos fundamentales para el rendimiento de nuestro sistema, puesto elegí mantener el objeto en sí como parte de la DATA que viaja entre el server y el cliente y a su vez como identificador único de los elementos y también la cantidad de elementos a traer para mantener en memoria.

Estos elementos son:

  • public static final String OBJ = "OBJ"; que es la forma en que uso para mantener la UI simple en cuanto al objeto en sí.
  • public static final int BATCH_GRID_SIZE = 20; y este es el indicador de cuantos elementos va a buscar por solicitud.


?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import com.vaadin.data.Item;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.data.util.PropertysetItem;
import org.vaadin.addons.lazyquerycontainer.QueryDefinition;
import org.vaadin.addons.lazyquerycontainer.QueryFactory;
/**
 *  Created by aluis on 10/8/15.
 */
public abstract class BaseQueryFactory<BEANTYPE> implements QueryFactory {
    public static final String OBJ = "OBJ";
    public static final int BATCH_GRID_SIZE = 20;
    protected QueryDefinition queryDefinition;
    protected LazyQuery<BEANTYPE> lazyQuery;
    protected BaseQueryFactory(LazyQuery<BEANTYPE> lazyQuery) {
        this.lazyQuery = lazyQuery;
    }
    public abstract Item constructItem();
    @SuppressWarnings("unchecked")
    public Item constructItem(BEANTYPE beantype) {
        PropertysetItem item = new PropertysetItem();
        for (Object propertyId : this.queryDefinition.getPropertyIds()) {
            Object value = createProperty(propertyId, beantype);
            item.addItemProperty(propertyId, new ObjectProperty(value,
                    queryDefinition.getPropertyType(propertyId),
                    queryDefinition.isPropertyReadOnly(propertyId)
            ));
        }
        return item;
    }
    public abstract Object createProperty(Object propertyID, BEANTYPE dataObject);
    protected Object getDefaultProperty(Object propertyID) {
        return queryDefinition.getPropertyDefaultValue(propertyID);
    }
    public QueryDefinition getQueryDefinition() {
        return queryDefinition;
    }
    public void setQueryDefinition(QueryDefinition queryDefinition) {
        this.queryDefinition = queryDefinition;
    }
    public LazyQuery<BEANTYPE> getLazyQuery() {
        return lazyQuery;
    }
    public void setLazyQuery(LazyQuery<BEANTYPE> lazyQuery) {
        this.lazyQuery = lazyQuery;
    }
}
import com.vaadin.data.Item;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.data.util.PropertysetItem;
import org.vaadin.addons.lazyquerycontainer.QueryDefinition;
import org.vaadin.addons.lazyquerycontainer.QueryFactory;

/**
 *  Created by aluis on 10/8/15.
 */
public abstract class BaseQueryFactory<BEANTYPE> implements QueryFactory {

    public static final String OBJ = "OBJ";
    public static final int BATCH_GRID_SIZE = 20;
    protected QueryDefinition queryDefinition;
    protected LazyQuery<BEANTYPE> lazyQuery;

    protected BaseQueryFactory(LazyQuery<BEANTYPE> lazyQuery) {
        this.lazyQuery = lazyQuery;
    }

    public abstract Item constructItem();

    @SuppressWarnings("unchecked")
    public Item constructItem(BEANTYPE beantype) {
        PropertysetItem item = new PropertysetItem();
        for (Object propertyId : this.queryDefinition.getPropertyIds()) {
            Object value = createProperty(propertyId, beantype);
            item.addItemProperty(propertyId, new ObjectProperty(value,
                    queryDefinition.getPropertyType(propertyId),
                    queryDefinition.isPropertyReadOnly(propertyId)
            ));
        }
        return item;
    }

    public abstract Object createProperty(Object propertyID, BEANTYPE dataObject);

    protected Object getDefaultProperty(Object propertyID) {
        return queryDefinition.getPropertyDefaultValue(propertyID);
    }

    public QueryDefinition getQueryDefinition() {
        return queryDefinition;
    }

    public void setQueryDefinition(QueryDefinition queryDefinition) {
        this.queryDefinition = queryDefinition;
    }

    public LazyQuery<BEANTYPE> getLazyQuery() {
        return lazyQuery;
    }

    public void setLazyQuery(LazyQuery<BEANTYPE> lazyQuery) {
        this.lazyQuery = lazyQuery;
    }
}

y por último nuestro contenedor (Container)

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition;
import org.vaadin.addons.lazyquerycontainer.LazyQueryView;
import org.vaadin.addons.lazyquerycontainer.QueryFactory;
/**
 *  Created by aluis on 10/8/15.
 */
public abstract class BaseContainer extends LazyQueryContainer {
    public BaseContainer(QueryFactory queryFactory) {
        super(new LazyQueryDefinition(true, BaseQueryFactory.BATCH_GRID_SIZE, BaseQueryFactory.OBJ), queryFactory);
        addContainerProperty(BaseQueryFactory.OBJ, Object.class, null, false, false);
    }
    @SuppressWarnings("unused")
    public void addDebug() {
        addContainerProperty(LazyQueryView.DEBUG_PROPERTY_ID_QUERY_INDEX, Integer.class, 0, true, false);
        addContainerProperty(LazyQueryView.DEBUG_PROPERTY_ID_BATCH_INDEX, Integer.class, 0, true, false);
        addContainerProperty(LazyQueryView.DEBUG_PROPERTY_ID_BATCH_QUERY_TIME, Long.class, 0, true, false);
        addContainerProperty(LazyQueryView.PROPERTY_ID_ITEM_STATUS, Enum.class, 0, true, false);
    }
    public abstract void addColumns();
}
import org.vaadin.addons.lazyquerycontainer.LazyQueryContainer;
import org.vaadin.addons.lazyquerycontainer.LazyQueryDefinition;
import org.vaadin.addons.lazyquerycontainer.LazyQueryView;
import org.vaadin.addons.lazyquerycontainer.QueryFactory;

/**
 *  Created by aluis on 10/8/15.
 */
public abstract class BaseContainer extends LazyQueryContainer {

    public BaseContainer(QueryFactory queryFactory) {
        super(new LazyQueryDefinition(true, BaseQueryFactory.BATCH_GRID_SIZE, BaseQueryFactory.OBJ), queryFactory);
        addContainerProperty(BaseQueryFactory.OBJ, Object.class, null, false, false);
    }

    @SuppressWarnings("unused")
    public void addDebug() {
        addContainerProperty(LazyQueryView.DEBUG_PROPERTY_ID_QUERY_INDEX, Integer.class, 0, true, false);
        addContainerProperty(LazyQueryView.DEBUG_PROPERTY_ID_BATCH_INDEX, Integer.class, 0, true, false);
        addContainerProperty(LazyQueryView.DEBUG_PROPERTY_ID_BATCH_QUERY_TIME, Long.class, 0, true, false);
        addContainerProperty(LazyQueryView.PROPERTY_ID_ITEM_STATUS, Enum.class, 0, true, false);
    }

    public abstract void addColumns();
}

Una vez construidos todos estos elementos proceso a crear una implementación usando un DOMAIN (Student).

Creamos la consulta (Query) en un inicio:

?
01
02
03
04
05
06
07
08
09
10
11
12
13
import com.alsnightsoft.vaadin.testcontainer.BaseQuery
import com.alsnightsoft.vaadin.testcontainer.BaseQueryFactory
import com.alsnightsoft.vaadin.testcontainer.domains.Student
/**
 *  Created by aluis on 10/16/15.
 */
class StudentQuery extends BaseQuery<Student> {
    public StudentQuery(BaseQueryFactory baseQueryFactory) {
        super(baseQueryFactory)
    }
}
import com.alsnightsoft.vaadin.testcontainer.BaseQuery
import com.alsnightsoft.vaadin.testcontainer.BaseQueryFactory
import com.alsnightsoft.vaadin.testcontainer.domains.Student

/**
 *  Created by aluis on 10/16/15.
 */
class StudentQuery extends BaseQuery<Student> {

    public StudentQuery(BaseQueryFactory baseQueryFactory) {
        super(baseQueryFactory)
    }
}

Luego creamos el creador de consultas (QueryFactory)

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import com.alsnightsoft.vaadin.testcontainer.BaseQueryFactory
import com.alsnightsoft.vaadin.testcontainer.LazyQuery
import com.alsnightsoft.vaadin.testcontainer.domains.Student
import com.vaadin.data.Item
import org.vaadin.addons.lazyquerycontainer.Query
import org.vaadin.addons.lazyquerycontainer.QueryDefinition
/**
 *  Created by aluis on 10/16/15.
 */
class StudentFactory extends BaseQueryFactory<Student> {
    public StudentFactory(LazyQuery<Student> lazyQuery) {
        super(lazyQuery)
    }
    @Override
    Item constructItem() {
        return constructItem(new Student())
    }
    @Override
    Object createProperty(Object propertyID, Student dataObject) {
        switch (propertyID.toString()) {
            case OBJ:
                return dataObject
            case "id":
                return dataObject.getId()
            case "name":
                return dataObject.getName()
            case "description":
                return dataObject.getDescription()
            case "enabled":
                return dataObject.getEnabled()
        }
        return getDefaultProperty(propertyID)
    }
    @Override
    Query constructQuery(QueryDefinition queryDefinition) {
        this.queryDefinition = queryDefinition
        return new StudentQuery(this)
    }
}
import com.alsnightsoft.vaadin.testcontainer.BaseQueryFactory
import com.alsnightsoft.vaadin.testcontainer.LazyQuery
import com.alsnightsoft.vaadin.testcontainer.domains.Student
import com.vaadin.data.Item
import org.vaadin.addons.lazyquerycontainer.Query
import org.vaadin.addons.lazyquerycontainer.QueryDefinition

/**
 *  Created by aluis on 10/16/15.
 */
class StudentFactory extends BaseQueryFactory<Student> {

    public StudentFactory(LazyQuery<Student> lazyQuery) {
        super(lazyQuery)
    }

    @Override
    Item constructItem() {
        return constructItem(new Student())
    }

    @Override
    Object createProperty(Object propertyID, Student dataObject) {
        switch (propertyID.toString()) {
            case OBJ:
                return dataObject
            case "id":
                return dataObject.getId()
            case "name":
                return dataObject.getName()
            case "description":
                return dataObject.getDescription()
            case "enabled":
                return dataObject.getEnabled()
        }
        return getDefaultProperty(propertyID)
    }

    @Override
    Query constructQuery(QueryDefinition queryDefinition) {
        this.queryDefinition = queryDefinition
        return new StudentQuery(this)
    }
}

Y por último creamos nuestro contenedor simbólico.

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import com.alsnightsoft.vaadin.testcontainer.BaseContainer
import com.alsnightsoft.vaadin.testcontainer.LazyQuery
import com.alsnightsoft.vaadin.testcontainer.domains.Student
/**
 *  Created by aluis on 10/16/15.
 */
class StudentContainer2 extends BaseContainer {
    public StudentContainer2(LazyQuery<Student> lazyQuery) {
        super(new StudentFactory(lazyQuery))
        addColumns()
    }
    @Override
    public void addColumns() {
        addContainerProperty("id", Long.class, 0l, false, false)
        addContainerProperty("name", String.class, "", false, false);
        addContainerProperty("description", String.class, "", false, false);
        addContainerProperty("enabled", Boolean.class, true, false, false);
    }
}
import com.alsnightsoft.vaadin.testcontainer.BaseContainer
import com.alsnightsoft.vaadin.testcontainer.LazyQuery
import com.alsnightsoft.vaadin.testcontainer.domains.Student

/**
 *  Created by aluis on 10/16/15.
 */
class StudentContainer2 extends BaseContainer {

    public StudentContainer2(LazyQuery<Student> lazyQuery) {
        super(new StudentFactory(lazyQuery))

        addColumns()
    }

    @Override
    public void addColumns() {
        addContainerProperty("id", Long.class, 0l, false, false)
        addContainerProperty("name", String.class, "", false, false);
        addContainerProperty("description", String.class, "", false, false);
        addContainerProperty("enabled", Boolean.class, true, false, false);
    }
}

Entonces una vez terminado todo esto solo nos queda implementar!

?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import com.alsnightsoft.vaadin.testcontainer.containers.StudentContainer2
import com.alsnightsoft.vaadin.testcontainer.domains.Student
import com.alsnightsoft.vaadin.testcontainer.services.StudentService
import com.vaadin.event.SelectionEvent
import com.vaadin.grails.Grails
import com.vaadin.server.VaadinRequest
import com.vaadin.ui.Grid
import com.vaadin.ui.UI
import com.vaadin.ui.VerticalLayout
/**
 *  Created by Ing. Aluis Marte on 2/1/2015.
 */
class Demo2 extends UI {
    private VerticalLayout mainLayout
    private StudentContainer2 container
    private Grid tableDemo
    @Override
    protected void init(VaadinRequest vaadinRequest) {
        mainLayout = new VerticalLayout()
        container = new StudentContainer2(new LazyQuery<Student>() {
            @Override
            int getSize() {
                return Grails.get(StudentService).countStudents(true)
            }
            @Override
            List<Student> getItemsIds(int startIndex, int numberOfIds) {
                return Grails.get(StudentService).listStudents(true, startIndex, numberOfIds)
            }
            @Override
            int getFilteredSize() {
                return 0
            }
            @Override
            List<Student> getFilteredItemsIds(int startIndex, int numberOfIds) {
                return new ArrayList<Student>()
            }
        })
        tableDemo = new Grid()
        tableDemo.setSelectionMode(Grid.SelectionMode.SINGLE)
        tableDemo.setImmediate(true)
        tableDemo.setSizeFull()
        tableDemo.setFooterVisible(true)
        tableDemo.setContainerDataSource(container)
        tableDemo.setColumnOrder("id", "name", "description", "enabled")
        tableDemo.getColumn("id").setHeaderCaption("ID")
        tableDemo.getColumn("name").setHeaderCaption("NAME")
        tableDemo.getColumn("description").setHeaderCaption("DESC")
        tableDemo.getColumn("enabled").setHeaderCaption("ACTIVE")
        tableDemo.removeColumn("OBJ")
        tableDemo.addSelectionListener(new SelectionEvent.SelectionListener() {
            @Override
            void select(SelectionEvent selectionEvent) {
                println "Student ID: " + ((Student) tableDemo.getSelectedRow()).getId()
                println "Student Name: " + ((Student) tableDemo.getSelectedRow()).getName()
            }
        })
        tableDemo.clearSortOrder()
        mainLayout.addComponent(tableDemo)
        setContent(mainLayout)
    }
}
import com.alsnightsoft.vaadin.testcontainer.containers.StudentContainer2
import com.alsnightsoft.vaadin.testcontainer.domains.Student
import com.alsnightsoft.vaadin.testcontainer.services.StudentService
import com.vaadin.event.SelectionEvent
import com.vaadin.grails.Grails
import com.vaadin.server.VaadinRequest
import com.vaadin.ui.Grid
import com.vaadin.ui.UI
import com.vaadin.ui.VerticalLayout

/**
 *  Created by Ing. Aluis Marte on 2/1/2015.
 */
class Demo2 extends UI {

    private VerticalLayout mainLayout

    private StudentContainer2 container

    private Grid tableDemo

    @Override
    protected void init(VaadinRequest vaadinRequest) {
        mainLayout = new VerticalLayout()

        container = new StudentContainer2(new LazyQuery<Student>() {
            @Override
            int getSize() {
                return Grails.get(StudentService).countStudents(true)
            }

            @Override
            List<Student> getItemsIds(int startIndex, int numberOfIds) {
                return Grails.get(StudentService).listStudents(true, startIndex, numberOfIds)
            }

            @Override
            int getFilteredSize() {
                return 0
            }

            @Override
            List<Student> getFilteredItemsIds(int startIndex, int numberOfIds) {
                return new ArrayList<Student>()
            }
        })

        tableDemo = new Grid()
        tableDemo.setSelectionMode(Grid.SelectionMode.SINGLE)
        tableDemo.setImmediate(true)
        tableDemo.setSizeFull()
        tableDemo.setFooterVisible(true)
        tableDemo.setContainerDataSource(container)
        tableDemo.setColumnOrder("id", "name", "description", "enabled")
        tableDemo.getColumn("id").setHeaderCaption("ID")
        tableDemo.getColumn("name").setHeaderCaption("NAME")
        tableDemo.getColumn("description").setHeaderCaption("DESC")
        tableDemo.getColumn("enabled").setHeaderCaption("ACTIVE")
        tableDemo.removeColumn("OBJ")
        tableDemo.addSelectionListener(new SelectionEvent.SelectionListener() {
            @Override
            void select(SelectionEvent selectionEvent) {
                println "Student ID: " + ((Student) tableDemo.getSelectedRow()).getId()
                println "Student Name: " + ((Student) tableDemo.getSelectedRow()).getName()
            }
        })

        tableDemo.clearSortOrder()
        mainLayout.addComponent(tableDemo)

        setContent(mainLayout)
    }
}

Felicidades! ya tenemos TABLES o GRID que usan contenedores vagos!

Más información aquí
Source Implementación GrailsLazyContainer
Source GIT TestLazyContainerGrailsVaadin