New Features and Enhancements in JDAL Vaadin 2.0
JDAL Vaadin is a module for integrating JDAL in Vaadin framework. This article covers the new features that has been added in version 2.0.
- Spring Framework custom namespace for Vaadin.
- Box Form Builder.
- Automatic Data Binding.
- Injecting Spring Serializable Proxies into Vaadin Components.
- Vaadin UI Spring Scope
- Sample Application.
Spring Framework custom namespace for Vaadin
One of the major drawbacks of the previous version (1.3.1) is the high number of bean definitions required in spring configuration files. Version 2.0 includes a custom namespace that greatly simplifies the context configuration.
<vaadin:defaults />
Used to declare the default settings required for JDAL. Define the following beans:
- Vaadin Paginator: Default pager for tabless.
- Default Table Actions: Default actions for tables
- Default Gui Factory: Default Vaadin component factory.
- Custom Editor Configurer: Register defaultPropertyEditors for images and icons
<vaadin:table>
Used to declare a pageable table linked to an entity. Represents a considerable improvement over the previous version which required us to declare two beans per pageable table.
Now, declaring and configuring a server side pageable table is very straightforward:
<!-- 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>
Used to declare a button handler that load a bean from context as Vaadin View.
<vaadin:button-bar>
Used to declare a horizontal or vertical layout with a list of actions. For example to create the navigation var of sample application, we declare the following button bar:
<!-- 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
The support class for building forms in JDAL Swing BoxFormBuilder is now available for Vaadin!.
Automatic Form Data Binding
The data binding system of Swing module has been ported to Vaadin, including the support for JSR-303 annotations and spring validators.
If we follow the same name convention for model properties and controls, the data binding code is reduced to calling only the autobind() method.
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(); @Property(name="publishedDate") // map to publishedDate model property (the default) private DateField publishedDate = new DateField(); @Initializer(orderBy="name") // autofill with authors, sorted by name private ComboBox author = new ComboBox(); @Initializer(orderBy="name") // autofill with categories, sorted by 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; }
To enable automatic initialization and validation checks of annotations, we have to add the following bean definitions to the application context:
<!-- 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"/>
Injecting Spring Serializable Proxies into Vaadin Components
All Vaadin Components are linked to J2EE container session and must be serializable to allow session pasitivation and replication across cluster nodes.
This prevents injecting directly Spring beans into Vaadin Components as these are usually non serializable or they shouldn't be serialised at all. For this reason, Spring beans dependences are usually declared as transient and initialized after deserialization. Although this works, it doesn't seem a very natural way to integreate both frameworks.
To solve this problem, the JDAL approach replace the Spring bean dependences with AOP serializable proxies in the Spring context, or directly in Vaadin component fields as well. The proxies resolve the context dependences in the deserialization process, allowing container session pasitivation and replication.
Serializable Proxy Configuration
To enable serializable proxies use the following bean definitions in context configuration file:
<!-- Enable @SerializableProxy support --> <bean id="serializableAnnotationBeanPostProcessor" class="org.jdal.aop.config.SerializableAnnotationBeanPostProcessor" /> <bean id="serializableProxyAdvisor" class="org.jdal.aop.SerializableProxyAdvisor" scope="prototype"/>
To replace a bean in the Spring context globally, we can use the <jdal:serializable-proxy> tag in jdal spring custom namespace.
<bean id="someBean" class="some.package.SomeBean"> <property name="someProperty" value="someValue" /> <jdal:serializable-proxy /> <!-- Replace this bean with a serializable proxy --> </bean>
Or we can use @SerializableProxy annotation on the bean types.
@Component @SerializableProxy public class StoreService { ... }
Finally, we can replace only the reference in the component without touching the Spring context, just using the annotation in the autowired field.
public class MainLayout extends VerticalLayout { @Autowired @SerializableProxy private CustomerDao customerDao; }
Vaadin UI Spring Scope
JDAL provides a Spring scope for Vaadin UI instances. To use it, declare de scope in bean configuration file as follows
<!-- 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>
Sample Application
The sample application is a book library manager that shows the main JDAL features.
Online demo - Use admin/admin as user/password