Étiquette : GWT

Datasources en hosted mode avec GWT 1.6

Configuration des datasources en hosted mode avec GWT 1.6

La migration de GWT de la version 1.5 à la version 1.6 s’est accompagnée de changements en profondeur au niveau du hosted mode. En effet, celui-ci utilisait précédemment une version embarquée du serveur tomcat, qui a été remplacée dans la dernière version par jetty.

Ce changement a donc entraîné une restructuration au niveau de la configuration des projets et notamment de la déclaration des datasources *. Cet article va donc présenter la configuration des datasources en hosted mode avec GWT 1.6.

La première étape est de rajouter les jar jetty-naming-6.1.11.jar et jetty-plus-6.1.11.jar à notre projet. Ceux-ci permettront d’utiliser notre propre fichier de configuration de jetty. Si comme nous vous utilisez Maven pour gérer les dépendances de votre projet, rajoutez les deux entrées suivants à votre pom.xml :

<dependency>
<groupid>org.mortbay.jetty</groupid>
<artifactid>jetty-naming</artifactid>
<version>6.1.11</version>
</dependency>
<dependency>
<groupid>org.mortbay.jetty</groupid>
<artifactid>jetty-plus</artifactid>
<version>6.1.11</version>
</dependency>

Sinon, vous trouverez ces jars sur le site de jetty.
Il faut maintenant crée un fichier nommé jetty-web.xml qui contiendra la déclaration de nos datasources sous la forme suivante :

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
<Configure>
<New id="dataset1">
<Arg>java:comp/env/jdbc/dataset1</Arg> <Arg>
<New>
<Set name="Url">jdbc:mysql://localhost:3306/database1?autoReconnect=true</Set>
<Set name="User">username1</Set>
<Set name="Password">password1</Set>
</New>
</Arg>
</New>
<New id="dataset2">
<Arg>java:comp/env/jdbc/dataset2</Arg>
<Arg>
<New>
<Set name="Url">jdbc:mysql://localhost:3306/database2?autoReconnect=true</Set>
<Set name="User">username2</Set>
<Set name="Password">password2</Set>
</New>
</Arg>
</New>
</Configure>

C’est cette configuration qui va indiquer à quelle base de donnée correspond chacun des datasets, et les paramètres de connexion. Dans cette exemple, nous déclarons deux datasets qui utilisent les bases de donnée MySql database1 et database2 du serveur local.
Il faut ensuite référencer les datasources dans le web.xml pour que notre application soit en mesure de les utiliser :

<res-ref-name>java:comp/env/jdbc/dataset1</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<resource-ref>
<res-ref-name>java:comp/env/jdbc/dataset2</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>

Une fois cette étape réalisée, vous pouvez utiliser vos datasets au sein de votre application comme vous le feriez normalement :

DataSource source = (DataSource)ctx.lookup("java:comp/env/jdbc/dataset2");
Connection connection = source.getConnection();
//...

 

Caption Panel GWT

Les contraintes ergonomiques des IHM d’applications professionnelles sont bien plus souples que celles qui régissent la conception de portails de ventes en ligne.
L’offre Viaxeo proposant un moteur de génération de sites e-business intégrés in extenso dans son CRM multicanal, nous avons du développer les outils nous permettant de répondre efficacement aux canons graphiques actuels afin de nous conformer aux chartes graphiques de nos clients.

Le premier obstacle rencontré était inhérent à la physionomie rudimentaire de nos panels de bases. Point de formes géographiques fantaisistes ni d’habillage recherché sans passer par l’utilisation manuelle, au cas par cas, de groupes de widgets englobant et de feuilles de style.

GWT propose de palier à ces difficultés via le DecoratorPanel, un widget à 9 « conteneurs », 8 panels périphériques destinés à accueillir images et styles, et un SimplePanel central dévolu à l’ajout de contenu.
En voici un exemple d’utilisation. Le but initial étant de créer un Caption Panel à bords arrondis, dont le style du titre soit paramétrable.

public class CaptionPanel extends Composite {private VerticalPanel absolutePanel;
private DecoratorPanel titlePanel;
private DecoratorPanel bodyPanel;
private VerticalPanel verticalPanel;
private HorizontalPanel horizontalPanel;
public CaptionPanel(int width, int height, int titleWidth, int titleHeight, String title){
this(width, height, null, titleWidth, titleHeight, title, null);
}

public CaptionPanel(int width, int height, String style, int titleWidth, int titleHeight, String title, String titleStyle){
absolutePanel = new VerticalPanel();
titlePanel = new DecoratorPanel();
bodyPanel = new DecoratorPanel();
verticalPanel = new VerticalPanel();
horizontalPanel = new HorizontalPanel();
horizontalPanel.
setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
absolutePanel.
setSize(width+"px", (height + titleHeight)+"px");
if (titleStyle != null){
horizontalPanel.setStyleName(titleStyle);
titlePanel.setStyleName(titleStyle);
}
if (style != null){
bodyPanel.setStyleName(style);
}

titlePanel.setSize(titleWidth+"px", titleHeight+"px");
titlePanel.add(new Label(title));
bodyPanel.setSize(width+"px", (height-titleHeight/2)+"px");
bodyPanel.add(verticalPanel);
horizontalPanel.add(titlePanel);
absolutePanel.add(horizontalPanel);
absolutePanel.add(bodyPanel);
initWidget(absolutePanel);
}

public void addTitleStyle(String style){
horizontalPanel.setStyleName(style);
titlePanel.addStyleName(style);
}

public void addBodyStyle(String style){
bodyPanel.addStyleName(style);
}

public void addContent(Widget content){
verticalPanel.add(content);
}

public void clearContent(){
verticalPanel.clear();
}

Voici quelques exemples de rendu :

Caption Panel

GWT et PureMVC

Utilisation de PureMVC

La mise en œuvre d’une application RIA robuste, maintenable, réutilisable et évolutive nécessite l’utilisation des modèles de conceptions éprouvés. Notre application est réalisée en GWT basée sur une architecture multi-couche. Voici quelques unes des exigences auxquelles doit répondre notre application :

  • Faible couplage entre les différentes couches,
  • Chaque composant de l’application doit avoir un rôle clair et précis,
  • Accès transparent à des services distants ou co-localisés,
  • Une mise en cache des données utilisées fréquemment afin d’accroître les performances.

Afin de répondre à ces exigences, notre choix s’est porté sur le framework PureMVC. Ce framework met en œuvre le méta Pattern MVC. Il permet de découpler vues et modèles en instaurant entre eux un protocole d’échanges basé sur le pattern Observateur. Ce modèle de conception établit un type d’interaction Diffusion / Souscription.

Le framework PureMVC permet de séparer la couche Vue de notre application en trois tiers bien distincts : le modèle, la vue et le contrôleur. Ces trois tiers sont mis en œuvre par trois classes appelées ModelView et Controller. Elles constituent les acteurs centraux du framework. Ces acteurs collaborent entre eux par le biais d’une Façade.
La classe Façade détient les références des trois acteurs centraux. Chaque acteur est couplé avec des collaborateurs. La façade initialise les acteurs centraux et procède à une phase d’enregistrement des collaborateurs :

  • Model — registerProxy (<IProxy>)
  • Vue — registerMediator (<IMediator>)
  • Controller — registerCommand(<notificationName>, <ICommand>)

Le modèle détient des références des classes Proxy. Chaque proxy communique avec des services distants et sert de cache des données fréquemment utilisées. La vue détient des références des classes Mediator. Un médiateur représente des composants visuels. Enfin le contrôleur détient des références des classes Command. Les commandes peuvent interagir directement avec des classes Proxy ou envoyer des notifications.

Voici un schéma qui illustre la manière dont sont orchestrés les échanges entre les différents acteurs du framework PureMVC :

PureMVC

Compilation GWT et gestion de la mémoire

Nous utilisons le plugin maven googlewebtoolkit2, pour compiler notre application GWT, sans paramétrage spécifique ou optimisation.

La taille de l’application devenant de plus en plus importante, nous sommes tombés sur un classique

java.lang.OutOfMemoryError: PermGen space.

Premier problème trouver comment modifier les paramètres de la JVM.

Il suffit de rajouter dans la configuration du plugin la balise extraJvmArgs

Malgré une augmentation de l’espace mémoire alloué, nous avions toujours la même erreur (des 32Mo par défaut à 1024Mo). Nous avons donc essayé de jouer sur les autres paramètres impactant le PermGen space. Et en rajoutant le –Xmn50M nous somme parvenu à compiler à nouveau l’application.


Mais nos espoirs ont été de courte durée…Nous nous sommes trouvés avec une compilation aléatoire qui passait de temps en temps…

Nous avons donc choisit de tester la compilation avec une JVM concurrente de celle de Sun : JRockit (initialement BEA, Oracle depuis 2008).
JRockit se veut plus performante que la JVM SUN. Il y a peu d’études intépendantes qui montrent les apports réels en termes performance. Nous avons donc décidé de réaliser les tests nous même.
Suite à quelques essais, force est de constater qu’avec cette JVM nous avons une compilation qui fonctionne sans aucun problème toutes les nuits avec un temps de traitement légèrement inférieur.

GWT et Drag and Drop

Le « glisser-déposer », ou Drag and Drop, n’est pas géré nativement par GWT.

Afin de palier à ce manque, un certains nombres d’implémentations ont été élaborées.
Le choix de Viaxoft s’est porté sur dnd-gwt, et ceux pour deux raisons :

  • Cette API présente l’avantage d’être non invasive, de n’impliquer aucune génération de code annexe.
  • « dnd » permet la réutilisation directe des widgets de GWT et de nos composites.

Cette note survole les grands principes autours desquels s’articule cette librairie.

containingPanel.setPixelSize(w, h);
containingPanel.getElement().getStyle().setProperty("position","relative");
PickupDragController dragController = new PickupDragController(containingPanel, true);//Paramétrage du comportement DnD
//On interdit les selections mulitples et aux Widgets draggables d'être déposés hors des drop zones
dragController.setBehaviorBoundaryPanelDrop(false);
dragController.setBehaviorMultipleSelection(false);

Une fois un DragController défini, n’importe quel widget implémentant SourcesMouseEvents peut être transformé en élément « draggable ». On pourrait donc penser être limité aux Image, Label et FocusPanel. Cependant tout composite implémentant l’interface est lui-même éligible. Le DragController permet notamment de distinguer un composite dans son ensemble de sa partie réalisant le Drag & Drop.

class TextAndPicto extends Composite implements SourcesMouseEvents{
TextField text;
Image picto;
public LabelAndPicto(TextField text, Image picto)
{
this.text = text;
this.picto=picto;
HorizontalPanel mainPanel = new HorizontalPanel();
mainPanel.add(picto);
mainPanel.add(text);
initWidget(mainPanel);
}

public void addMouseListener(MouseListener listener) {
picto.addMouseListener(listener);

}

public void removeMouseListener(MouseListener listener) {
picto.removeMouseListener(listener);
...

TextAndPicto w = new TextAndPicto(new TextBox(), new Image("pictoViaxeo/clients.gif"));
dragController.makeDraggable(w,w.getImage());
Enfin le DropController permet de définir le comportement des widgets sur lesquels pourront être déposés les éléments « draggables ». Le constructeur prend en paramètre le widget qui servira de « drop zone », et les 4 méthodes suivantes sont définies par l’interface afin de gérer son comportement : onEnter, onLeave, onDrop et onPreviewDrop. Les deux premières sont appelées lorsqu’un item est « dragué » au dessus de la drop zone. La troisième détermine les actions à prendre consécutivement à la dépose du dit item. Et enfin, onPreviewDrop permet d’effectuer des contrôles préalables avant l’exécution d’onDrop.
  public class DropZoneListener extends SimpleDropController {
private VerticalPanel panel;

public DropZoneListener(Widget dropTarget) {
super(dropTarget);
panel = (VerticalPanel)dropTarget;
}

public void onDrop(DragContext context) {
super.onDrop(context);
for(SmartWidget smartWidget : (List)context.selectedWidgets)
panel.add(smartWidget);
}

public void onEnter(DragContext context) {
super.onEnter(context);
panel.addStyleName("viaxeo-DnD-Liste-Header-seleted");
}

public void onLeave(DragContext context) {
super.onLeave(context);
panel.removeStyleName("viaxeo-DnD-Liste-Header-seleted");
}

public void onPreviewDrop(DragContext context) throws VetoDragException {
super.onPreviewDrop(context);
}

}
Dnd-gwt propose un large panel de DropController allant du GridConstrainedDropController (gestion du drop par grilles) au FlexTableRowDropController (ajout/suppression dynamique de lignes dans une FlexTable).

GWT et éditeur de texte riche : impossible ?

Au cours du développement du produit, nous avons été confronté à un problème imprévu : l’intégration d’un éditeur de texte riche au sein d’une application écrite en GWT.

En effet, nous désirions laisser la possibilité à l’utilisateur de créer ou modifier une page web en mode WYSIWYG directement depuis l’application, mais l’éditeur proposé par GWT ne nous paraissait pas suffisant.

Après une rapide analyse des solutions existantes, nous avons tout d’abord décidé d’utiliser FCKeditor, celui-ci semblant offrir le plus grand nombre de fonctionnalités sur le marché à l’heure actuelle.
Nous sommes pour cela repartis de gwt-html-editor que nous avons adaptée à nos besoins. Cependant, si cette solution fonctionnait parfaitement lors de nos tests en hosted mode, dès le premier test de l’application déployée, nous avons rencontré des problèmes :

  • comportements différents selon les navigateurs
  • problèmes lors du premier chargement du composant
  • problèmes lors du rechargement du composant

Ces problèmes étant impossibles à résoudre facilement, nous avons décidé de tester les solutions concurrentes : XinhaTinyMCE et Yahoo! editor.
Mais celles-ci se sont avérées souffrir des même problèmes que FCKeditor. En effet, ces éditeurs ont été conçus pour fonctionner au sein d’une application web « classique », et ne sont pas adaptés pour une application entièrement enAJAX, comme celles réalisées en GWT.

Suite à ce constat, nous avons choisi de privilégier une solution hybride : la visualisation du texte riche se fait dans une iframe au sein de l’application, mais son édition se fait dans une page web classique présentée en popup.

Le composant s’articule autour de trois fichiers :

  • FCKRichEditorImpl.java, le composant en lui-même
  • iframeFckEditor.html, la page web affichant le rendu au sein d’une iframe (l’iframe permet d’avoir un style propre à la page affichée, ce que ne permettait pas l’utilisation d’un setInnerHTML (), par exemple)
  • pageFckEditor.html, la page en popup permettant d’éditer le texte grâce à FCKeditor

Cette solution fonctionne parfaitement et ne souffre pas des problèmes cités précédemment, mais elle en apporte cependant deux nouveaux :

  • moins bonne intégration de l’édition de texte avec le reste de l’application
  • possibilité de problème avec les bloqueurs de popup

C’est pourquoi nous restons à l’écoute du marché, et surveillons avec attention les nouvelles solutions disponibles.

Si vous désirez le code de notre solution n’hésitez pas à nous contacter

GWT, Tomcat, Eclipse et Cypal Studio

GWT et son hosted mode

GWT offre un mode de de développement « embarqué » (le hosted mode) qui permet de faire tourner l’application au sein de la machine virtuelle Java et ainsi garder tous les avantages d’un IDE tel qu’Eclipse et notamment le debugger.

Mais pour l’intégration de module plus avancés tels que la sécurité avec Acegi, il est primordial de déployer l’application sur un tomcat externe afin de configurer au mieux ce dernier. Voici un lien vers un très bon post, détaillant au sein d’Eclipse la création d’un projet GWT en s’appuyant sur le plugin Cypal. Ceux qui ont déjà galéré sur ce type de déploiement apprécieront :
Creating your first GWT app with Cypal Studio for GWT.
Merci à Prakash pour ce billet.

L’intégration d’Acegi au trio GWT, Spring, Hibernate fera l’objet d’un prochain post.

Loading...
X