Nuevas características y mejoras en JDAL 2.0

JDAL Vaadin es un módulo de JDAL que facilita la integración de Spring con el framework Vaadin. Este artículo cubre las novedades que se han añadido en la versión 2.0.



Espacio de nombres personalizado para Spring Framework


Uno de los principales inconvenientes de la versión anterior (1.3.1) es el número de definiciones que es necesario incorporar al fichero de configuración del contexto de Spring. La versión 2.0 incluye un espacio de nombres personalizado que simplifica considerablemente la configuración del contexto.

<vaadin:defaults />

Se utiliza para declarar la configuración por defecto de los beans necesarios para JDAL. Define los siguientes elementos:

<vaadin:table>

Se utiliza para declarar una tabla paginable vinculada a una entidad. Representa una mejora considerable respecto a la versión anterior que nos obligaba a declarar dos beans por tabla paginable.

Ahora, este elemento nos permite configurar y añadir la tabla de forma mucho más sencilla:

<!-- Book Table Definition -->   
<vaadin:table entity="org.jdal.samples.model.Book" filter-form="bookFilterEditor" scope="prototype">
    <vaadin:columns>
        <vaadin:column name="id" display-name="ID" width="60" align="center"/>
        <vaadin:column name="name" display-name="Name" width="300" align="left" />
        <vaadin:column name="author" display-name="Author" width="150" align="left" sort-property-name="author.name" />
        <vaadin:column name="category" display-name="Category" width="200" align="left" sort-property-name="category.name" />
        <vaadin:column name="isbn" display-name="ISBN" width="150" align="left" />
        <vaadin:column name="publishedDate" display-name="Published Date" width="150" property-editor="customDateEditor"/>
    </vaadin:columns>
</vaadin:table>
 

<vaadin:navigator-action>

Se utiliza para declarar un manejador de un botón que cargará un View de Vaadin en el navegador


<vaadin:button-bar>

Se utiliza para declarar una barra de navegación compuesta de botones. Por ejemplo, la aplicación de ejemplo utiliza la siguiente declaración para crear la barra superior de navegación.

  <!-- Top button menu -->
  <vaadin:button-bar id="buttonBar" scope="prototype">
       <vaadin:actions>
           <vaadin:navigator-action caption="Books" view-name="bookMainView" 
               icon="classpath:/org/freedesktop/tango/22x22/mimetypes/x-office-address-book.png"/>
           <vaadin:navigator-action caption="Authors" view-name="authorMainView" 
               icon="classpath:/org/freedesktop/tango/22x22/apps/preferences-desktop-theme.png"/>
           <vaadin:navigator-action caption="Categories"  view-name="categoryMainView"
               icon="classpath:/org/freedesktop/tango/22x22/places/folder.png" />
           <vaadin:navigator-action caption="Users" view-name="userMainView" 
               icon="classpath:/org/freedesktop/tango/22x22/apps/system-users.png"/>
           <vaadin:navigator-action caption="About" view-name="aboutMainView" 
               icon="classpath:/org/freedesktop/tango/22x22/apps/help-browser.png"/>
           <bean class="org.jdal.vaadin.ui.action.ExitAction" p:caption="Exit" 
                p:icon="classpath:/org/freedesktop/tango/22x22/actions/system-shutdown.png" />
      </vaadin:actions>
   </vaadin:button-bar>
 
  <!-- Make main views ui scoped to avoid recreating thems in every page change -->
  <bean id="bookMainView" class="org.jdal.samples.vaadin.BookMainView" scope="ui" />
  <bean id="authorMainView" class="org.jdal.samples.vaadin.AuthorMainView" scope="ui" />
  <bean id="categoryMainView" class="org.jdal.samples.vaadin.CategoryMainView" scope="ui" />
  <bean id="aboutMainView" class="org.jdal.samples.vaadin.AboutMainView" scope="ui" />
  <bean id="userMainView" class="org.jdal.samples.vaadin.UserMainView" scope="ui" />

BoxFormBuilder


La clase de soporte para construcción de formularios del módulo Swing BoxFormBuilder ahora también está disponible para Vaadin.


Binding Automático de Formularios


El sistema de data binding del módulo Swing, se ha portado a Vaadin, incluyendo el soporte para validación de anotaciones JSR-303 y las nuevas anotaciones de JDAL @Property e @Initializer

Si seguimos el convenio del mismo nombre para las propiedades del modelo y los controles. El código de data binding se reduce únicamente a llamar al método autobind().

public class BookView extends AbstractView<Book> {
 
	// We use autobind jdal feature, name the controls like Book properties
	private TextField name = new TextField();
	private TextField isbn = new TextField();
	private DateField publishedDate = new DateField();
	@Initializer(orderBy="name")
	private ComboBox author = new ComboBox();
	@Initializer(orderBy="name")
	private ComboBox category = new ComboBox();
 
	public BookView() {
		this(new Book());
	}
 
	public BookView(Book model) {
		super(model);
	}
 
	@PostConstruct
	public void init()  {
		// Just our binding code!
		autobind();
	}
 
	@Override
	protected Component buildPanel() {
		BoxFormBuilder fb = new BoxFormBuilder();
		fb.setDefaultWidth(SimpleBoxFormBuilder.SIZE_FULL);
		fb.setFixedHeight();
 
		fb.row();
		fb.add(name, getMessage("Book.title"));
		fb.row();
		fb.startBox();
		fb.row();
		fb.add(author, getMessage("Book.author"));
		fb.add(category, getMessage("Book.category"));
		fb.endBox();
		fb.row();
		fb.startBox();
		fb.row();
		fb.add(isbn, getMessage("Book.isbn"));
		fb.add(publishedDate, getMessage("Book.publishedDate"), 120);
		fb.endBox();
 
		return fb.getForm();
	}
 
	 // The view name is shown as title on ViewDialog.
	@Override
	public String getName() { 
		Book model = getModel();
 
		if (model != null && model.getId() != null) {
			return model.getName();
		}
 
		return null;
	}

Para activar la inicialización automática de los controles y la validación de las anotaciones tenemos que añadir las siguientes definiciones al contexto de la aplicación.


<!-- Book Form Editor -->
<bean id="bookEditor" class="org.jdal.samples.vaadin.BookView"  scope="prototype">
	<property name="controlInitializer" ref="controlInitializer" />
	<property name="width" value="600" />
	<property name="height" value="300" />
	<property name="persistentService" ref="bookService" />
	<property name="errorProcessors">
		<list>
			<ref bean="errorProcessor" />
		</list>
	</property>
	<property name="validator" ref="validator" />
</bean>
 
<!-- Global Beans -->
 
<!-- Initialize contros by looking at JPA annotations -->
<bean id="controlInitializer" class="org.jdal.vaadin.ui.bind.VaadinControlInitializer">
        <property name="persistentService" ref="contextPersistentService" />
</bean>
 
<!-- Spring JSR-303 validator -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
 
<!-- Error Processor, set control errror messages after validation -->
<bean id="errorProcessor" class="org.jdal.vaadin.ui.bind.UserErrorProcessor" />
 
<!-- View Dialog Prototype -->
<bean id="viewDialog" class="org.jdal.vaadin.ui.form.ViewDialog" scope="prototype"/>

Finalmente el resultado del formulario mostrando los errores de validación.





Proxies serializables en los componentes Vaadin


Todos los componentes de Vaadin están vinculados a la sesión del contenedor J2EE y deben de poderse serializar para permitir tanto la pasivación de la sesión en una sola JVM como la replicación de sesiones entre los nodos de un cluster.

Esto impide en principio injectar directamente los beans manejados por el contendor de Spring en los Componentes ya que estos generalmente o bien no son serializables o bien no deberían serializarse. Por este motivo es habitual declarar las dendencias de los beans de Spring como transient e inicializar los componentes después de la deserialización. Aunque funciona, es una forma poco natural de integrar ambos frameworks además de que supone un trabajo extra.

La aproximación de JDAL Vaadin para resolver el problema es reemplazar, bien los beans manejados por Spring o bien las referencias injectadas en los componentes Vaadin por proxies serializables mediante AOP. Al deserializarse, los proxies se actualizan recargandose del contexto de Spring, permitiendo tanto la pasivación como la replicación de sesiones entre los nodos de un cluster.

Configuración de proxies serializables

Para activar el soporte de proxies serializables, utilice la siguiente configuración.

<!-- Enable @SerializableProxy support -->
<bean id="serializableAnnotationBeanPostProcessor" class="org.jdal.aop.config.SerializableAnnotationBeanPostProcessor" />
<bean id="serializableProxyAdvisor" class="org.jdal.aop.SerializableProxyAdvisor" scope="prototype"/>

Para reemplazar un bean por un proxy de forma global en contexto de Spring se puede utilizar el tag <jdal:serializable-proxy> del espacio de nombres de JDAL

<bean id="someBean" class="some.package.SomeBean">
    <property name="someProperty" value="someValue" />
    <jdal:serializable-proxy />  <!-- Replace this bean with a serializable proxy -->
</bean>

O bien mediante la anotación @SerializableProxy en la declaración de clase

@Component
@SerializableProxy
public class StoreService {
    ...
}

Finalmente, para reemplazar únicamente la referencia en un componente Vaadin dejando intacto el bean en el contexto de Spring, se puede utilizar la anotación en los miembros de la clase.

public class MainLayout extends VerticalLayout {
 
    @Autowired
    @SerializableProxy
    private CustomerDao customerDao;
}

Scope de Spring para Vaadin UIs

JDAL proporciona un scope de Spring vinculado a las instancias UI de Vaadin. para activarlo utilice la siguiente declaración en fichero de configuración

<!-- Vaadin scoped beans are linked to UI class instances -->
<bean id="vaadinScopeConfigurer" class="org.springframework.beans.factory.config.CustomScopeConfigurer">
     <property name="scopes">
         <map>
             <entry key="ui" value="org.jdal.vaadin.beans.VaadinScope" />
         </map>
     </property>
</bean>

Aplicación de ejemplo


La aplicación de ejemplo consiste una librería (de libros) que muestra las principales características de JDAL.

Puede descargar el código del ejemplo del repositorio jdal-samples alojado en github.

Online demo - Use admin/admin como usuario/contraseña