MVP avec Vaadin 7 et CDI

Publié dans: 

Pour les admirateurs de la plateforme JEE, Vaadin propose l'addon « Vaadin CDI » qui permet de profiter de l'une des nouveautés de la plateforme JEE6, à savoir l'api CDI 1.0 combiné avec le framework Vaadin et sa large palette de composants graphique.

CDI 1.0

L'api CDI 1.0 propose principalement les services de gestion du cycle de vie des « Beans » dans un contexte de conteneur léger et l'injection de dépendances d'une manière typé grâce aux annotations.

Ces « Beans » sont regroupé dans des scopes.

 

L'api CDI propose des scopes par défaut, à l’instar de « request scope », « session scope » et « application scope », et permet de créer d'autre scopes métier personnalisés.

D’autres services font partie aussi du périmètre de l'api CDI à savoir l'implémentation de certain patron de conception comme ceux du Singleton, Factory, Decorator ou encore Observer.
Ce dernier permettant aux « Beans » de communiquer via l'émission et l'interception des événements (Events).

 

L'api CDI 1.0 est présente dans la plupart des serveurs d'application certifié JEE6.

Vaadin CDI addon

Avec Vaadin 7, l’interface graphique d’une application est un arbre de composants graphique regroupés tous sous une instance de la classe « UI » (com.vaadin.ui.UI). On trouve ainsi que pour chaque onglet du navigateur web, une seule instance de la classe « UI » est rendu.

 

Vaadin CDI addon propose, quant à lui, un scope personnalisé pour les instances de la classe « UI » de Vaadin 7. Ceci étant possible en annotant les classe héritant de « UI » par l'annotation @CDIUI que propose l'addon.

 

Ce qui fait que l’addon Vaadin CDI vise à avoir pour chaque instance d'une application Vaadin ses propres composants graphique qui peuvent appartenir à son propre scope CDI.

Déclarer ainsi des composants Vaadin dans le même scope de son UI se fait via l'annotation @UIScope.
Ces composant peuvent être injecté dans l'instance UI ou dans d'autre instances de composants graphique via l’injection des dépendances de CDI par le biais de l’annotation @Inject.

MVP avec Vaadin CDI

Dans l'exemple qui suit, on a essayé de reprendre l'implémentation du design patter MVP avec Vaadin et Spring, présenté dans l'article suivant : http://www.iptech-group.com/blog/mvp-avec-vaadin-7-et-spring, et de remplacer l’utilisation de Spring par CDI 1.0 et l'addon Vaadin CDI notamment dans l'utilisation de l'injection de dépendance et la communication événementielle entre les présentateurs (Presenters).

 

L’application d’exemple présente trois écrans ou vues gérée chacune par trois présentateurs. Chaque présentateur possède une référence sur la vue qu’il contrôle et communique avec les autres présentateurs via l’émission d’événements pour leurs notifier des interactions utilisateurs avec l’interface de sa vue.

 

 

Techniquement, chaque présentateur reçoit la référence de l’objet vue qu’il contrôle via l’injection de dépendance par le conteneur en rencontrant l'annotation @Inject.

@UIScoped
public class ContactsListPresenter extends AbstractPresenter implements IContactsListPresenter{

@Inject
private IContactsListView contactsListView;
...

Ces présentateurs comme les vues sont instanciés par le conteneur lorsqu'ils sont demendé pour une résolution de dépendence.

De ce fait, l'initialisation des vues lors de leurs création ou le rattachement des écouteurs sur l'interaction utilisateur avec l'interface que controle un présentateur peuvent etre assuré par le conteneur via les méthodes annoté par l'annotation @PostConstruct.

public abstract class AbstractView implements View, Serializable{
	
	@PostConstruct
	private void init() {
		initView();
	}
	
	protected abstract void initView();
}

 

public abstract class AbstractPresenter implements Presenter, Serializable {
	
	@PostConstruct
	private void bind() {
		bindEvents();
	}
	
	protected abstract void bindEvents();
}

La méthode bindEvents() présente dans la classe AbstractPresenter enregistre des écoutours sur l'interaction utilisateur avec les composants graphique de la vue que le présentateur controle.

En interceptant cet interaction, le présentateur peut agir sur sa vue pour y apporter des changements ou lancer un événement pour notifier les autres présentateurs de cette interaction pour qu'ils agissent sur leurs vues respectives si cette interaction l'exige.

@UIScoped
public class ContactsListPresenter extends AbstractPresenter implements IContactsListPresenter{

	@Inject
	private IContactsListView contactsListView;
	
	@Inject
	private Event<ContactSelectionEvent> contactSelectionEvent;
	
	@Override
	public void bindEvents() {
		contactsListView.getContactsListTable().addItemClickListener(new ItemClickListener() {
			
			@Override
			public void itemClick(ItemClickEvent event) {
				Integer contactId = (Integer) event.getItemId();
				contactSelectionEvent.fire(new ContactSelectionEvent(ContactsListPresenter.this, contactId));
			}
		});
	}
...


A la ligne 17, ContactListPresenter lance l'événement ContactSelectionEvent portant l'information contactId. C'est via l'object Event<ContactSelectionEvent> contactSelectionEvent; instancié et injecté par le conteneur CDI dans le présentateur.

Pour intercepter cet événement, voici le code a mettre dans ContactPresenter: 

@UIScoped
public class ContactPresenter extends AbstractPresenter implements IContactPresenter {

	...

	private void onContactSelectionEvent(@Observes ContactSelectionEvent event) {
		Contact contact = ContactUtils.getContactById(event.getContctId());
		if(contact != null){
			contactView.setContact(contact);
		}
	}

...

L'annotation @Observe indique au conteneur CDI qu'il s'git d'une méthode qui écoute et intercepte les événements de type ContactSelectionEvent.

 

On notera aussi que les présentateurs comme les vues sont marqué par l'annotation @UIScoped, ce qui indique au conteneur CDI qu'il appartiennent au scope d'une instance de la classe "UI". On aura ainsi pour chaque "UI" ses propre présentateurs et vues.

La classe UI doit etre marqué par l'annotation de l'addon Vaadin CDI : @CDIUI

@CDIUI
public class MyVaadinUI extends UI {

	@WebServlet(value = "/*", asyncSupported = true, 
			initParams = {@WebInitParam(name = Constants.SERVLET_PARAMETER_UI_PROVIDER, value="com.vaadin.cdi.CDIUIProvider")})
	@VaadinServletConfiguration(productionMode = false, ui = MyVaadinUI.class, widgetset = "com.iptech.rm.AppWidgetSet")
	public static class Servlet extends VaadinServlet {
	}

	@Inject
	private IApplicationPresenter applicationPresenter;

	@Override
	protected void init(VaadinRequest request) {
		setContent(applicationPresenter.getDisplay().getViewRoot());
	}
}

 

Vous pouvez continuer à explorer l'application d'exemple en téléchargeant le projet eclipse ci-dessous ou le fichier war de l'application:
- Projet eclipse: vaadin-mvp-cdi.rar

- WAR: vaadin-mvp-cdi-0.0.1-SNAPSHOT.war

 

Et voici, avant de terminer, quelque liens utile sur l'utilisation de l'addon Vaadin CDI: 

- Vaadin CDI integration: https://vaadin.com/wiki/-/wiki/Main/Vaadin-CDI-integration

- Using Vaadin CDI with JAAS authentication: https://vaadin.com/blog/-/blogs/using-vaadin-cdi-with-jaas-authentication