Vous n'êtes pas connecté. Connexion
|
|
TP5 (long) : étude de la librairie GWTDe $1IntroductionNous allons étudier la librairie GWT de Google utilisée par un nombre important de services Google tels que gmail ou google agenda. Cette tehnologie permet de développer des applications web faisant appel à des web services REST et des clients Ajax tout en développant exclusivement en Java. Le code serveur sera transformé en fichiers .class alors que le code client sera compilé en javascript. Comme GWT compile du java vers du javascript et ce pour les différents navigateurs du marché, la compilation peut être longue sur des ordinateurs peu puissants. Vous êtes prévenus ! En revanche le code compilé est très compact et bien optimisé. Les applications générées sont légères, etc. Premiers pas avec GWT
Utiliser l'application GWT Showcase comme source de nombreux exemples de codePremière étape : exécuter l'application en ligne
Jouez avec, regardez les sources des démos en cliquant sur l'onglet "source" Seconde étape (à faire à la maison car longue) : compiler et exécuter l'application dans netbeansAttention, la compilation peut durer 15 minutes ! Il est cependant intéressant, même si vous ne le compilez pas, d'avoir le projet ouvert dans netbeans car il est truffé de bouts de code réutilisables. C'est une mine d'exemple dont on se sert et ressert régulièrement. Le projet est dans l'installation de GWT, dans le répertoire samples/showcase. La vidéo youtube qui montre comment installer, compiler et exécuter l'application de démonstration de GWT (GWT Showcase, aussi testable en ligne ici : http://gwt.google.com/samples/Showcase/Showcase.html) :
Attention, il faudra aussi modifier la ligne suivante dans le fichier gwt.properties (par defaut ul y a gwt.version=2.1 or le plugin netbeans actuel ne reconnait que 2.0) : # GWT version: 1.5,1.6,1.7 or 2.0 gwt.version=2.0 Bien sûr, vous remplacerez les chemins par ceux de votre installation de GWT 2.1.0 ! Pour info, voici le fichier gwt.properties que j'ai dans mon projet : # The names of the modules to compile (separated by a space character) gwt.module=com.google.gwt.sample.showcase.Showcase # Folder within the web app context path where the output # of the GWT module compilation will be stored. # This setting is only used for GWT 1.5. For newer versions please use # the rename-to attribute in the GWT module file (.gwt.xml). gwt.output.dir=/org.yournamehere.Main # Script output style: OBF[USCATED], PRETTY, or DETAILED gwt.compiler.output.style=OBF # Additional JVM arguments for the GWT compiler gwt.compiler.jvmargs=-Xmx256M # Specifies the number of local workers to use whe compiling permutations and module(s) gwt.compiler.local.workers=1 # The level of logging detail: ERROR, WARN, INFO, TRACE, DEBUG, gwt.compiler.logLevel=WARN # Script output style: OBF[USCATED], PRETTY, or DETAILED gwt.shell.output.style=OBF # The level of logging detail: ERROR, WARN, INFO, TRACE, DEBUG, gwt.shell.logLevel=WARN # Additional JVM arguments for the GWT shell/GWT hosted mode (GWT 1.6) # Add -d32 here and use at least GWT 1.7.1 to debug on a Mac # (32-bit JRE is required by GWT for debugging) gwt.shell.jvmargs=-Xmx256M # GWT version: 1.5,1.6,1.7 or 2.0 gwt.version=2.0 # GWT 2.0 only # Specifies the TCP port for the code server gwt.shell.code.server.port=9997 # GWT 2.0 only # Specifies the TCP port for the embedded web server gwt.shell.port=8888 # Additional GWT compiler arguments # GWT 2.0 compiler supports these: # -workDir The compiler's working directory for internal use (must be writeable; defaults to a system temp dir) # -gen Debugging: causes normally-transient generated types to be saved in the specified directory # -ea Debugging: causes the compiled output to check assert statements # -XdisableClassMetadata EXPERIMENTAL: Disables some java.lang.Class methods (e.g. getName()) # -XdisableCastChecking EXPERIMENTAL: Disables run-time checking of cast operations # -validateOnly Validate all source code, but do not compile # -draftCompile Enable faster, but less-optimized, compilations # -compileReport Create a compile report that tells the Story of Your Compile # -localWorkers The number of local workers to use when compiling permutations # -extra The directory into which extra files, not intended for deployment, will be written gwt.compiler.args= # Additional JVM arguments for JUnit tests gwt.test.jvmargs=-Xmx256M # Additional arguments for the GWT shell # e.g. -bindAddress 0.0.0.0 since GWT 2.0.1 gwt.shell.args= GWT et JPA, session et entity beansModification du fichier gwt.properties pour indiquer qu'on est en gwt 2.0 pas 2.1Le plugin gwt4nb n'est pas totalement à jour, il ne supporte que gwt 1.5, 1.6, 1.7 et 2.0, pas encore 2.1. Pour qu'il compile correctement le projet, modifiez la ligne suivante du fichier gwt.properties, dans "configuration files" # GWT version: 1.5,1.6,1.7 or 2.0 gwt.version=2.0 Ajout d'un entity bean Etudiant au projetComme nous avons fait dans les projets précédents, ajoutez une classe entity au projet, n'oubliez pas de créer une persistence unit en mode "create" sur la base de données jdbc/sample. Vous placerez cet entity bean dans un package "entities". Vous devriez obtenir ceci : Maintenant rajoutez deux "propriétés" nom et prénom à la classe Etudiant.java. Vous pouvez utiliser le wizard "insert code/add property", cela vous génèrera la déclaration des attributs mais aussi les get et set. Vous devez avoir le début de la classe Etudiant.java qui ressemble à ceci : @Entity public class Etudiant implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; private String nom; private String prenom; /** * Get the value of prenom * * @return the value of prenom */ public String getPrenom() { return prenom; }... Ajout d'un session bean GestionnaireEtudiants.javaRajoutez au projet un session bean stateless, sans interfaces (pour simplifier), que vous nommerez GestionnaireEtudiants.java. Si vous ne voyez pas "add session bean" dans le menu pour ajouter des éléments au projet, allez dans "other/java EE/Session bean", cf la capture d'écran ci-dessous : Dans ce session bean, vous ajouterez un entity manager et une méthode pour insérer un étudiant dans la base de données : @Stateless public class GestionnaireEtudiants { @PersistenceContext EntityManager em; public Etudiant ajouteStudent(String prenom, String nom) { Etudiant s = new Etudiant(); s.setPrenom(prenom); s.setNom(nom); em.persist(s); return s; } } Création d'un service RPC Gwt EtudiantServiceImpl.javaRemarque : 'est en réalité une servlet qui répond à la manière d'un web service, on l'invoque depuis du code javascript compilé à partir du code java/GWT que vous allez écrire... Utilisez le wizard de netbeans pour créer ce service :
On remarque que plusieurs classes ont été générées. Celles du package gwt.jpa1.client.* seront compilées en javascript et tourneront dans un navigateur web. Celles du package gwt.jpa1.server.* implémentent les services côté serveur (ils sont appelées par une GWT Servlet). On va remplacer le code du service côté serveur (la classe EtudiantServiceImpl.java), au lieu de : public class EtudiantServiceImpl extends RemoteServiceServlet implements EtudiantService { public String myMethod(String s) { // Do something interesting with 's' here on the server. return "Server says: " + s; } } On va appeler le session bean gestionnaire d'étudiants pour insérer un étudiant (vous pouvez utiliser le wizard "call entreprise bean") : public class EtudiantServiceImpl extends RemoteServiceServlet implements EtudiantService { @EJB GestionnaireEtudiants gestionnaireEtudiants; public String ajouteEtudiant(String prenom, String nom) { Etudiant e = gestionnaireEtudiants.ajouteEtudiant(prenom, nom); return "Un nouvel étudiant a été ajouté par le service GWT : #" + e.getId() + " - " + e.getPrenom() + " " + e.getNom(); } } Comme les services GWT implémentent une interface du service (pour permettre la génération de code d'interception, voir le cours !), il faut également modifier les deux interfaces : celles qui sert au code généré côté serveur (le fichier EtudiantService.java) et celle qui est implémenté par le code javascript côté client (le fichier EtudiantServiceAsync.java). Tant que l'on a pas modifié ces deux interfaces, une erreur figure dans la classe EtudiantServiceImpl.java indiquant qu'elle n'implémente pas les bonnes interfaces. Fichier EtudiantService.java (notez l'annotation de code qui correspond à l'URL de la servlet qui implémente ce service, on va retrouver cet URL dans la classe d'exemple d'utilisation du service, et dans le fichier web.xml dans la partie "Servlets") : @RemoteServiceRelativePath("etudiantservice") public interface EtudiantService extends RemoteService { public String ajouteEtudiant(String prenom, String nom); } Fichier EtudiantServiceAsync.java : public interface EtudiantServiceAsync { public void ajouteEtudiant(String nom, String prenom, AsyncCallback<String> callback); } Noter que toutes les méthodes dans l'interface Asynchrone qui sera implémenté par le code compilé en javascript ont comme type de retour void ! C'est normal puisque la valeur de retour est fournie dans la fonction de callback ! Maintenant, nous allons modifier la classe d'exemple d'utilisation du service pour qu'elle fonctionne après que nous ayons modifié les interfaces d'utilisation. Prenez la peine d'étudier ce code ! Notez tout en bas dans la méthode getEtudiantService(), la présence de l'URL du service : public class EtudiantServiceUsageExample extends VerticalPanel { private Label labelReponseDuService = new Label(); private TextBox txtPrenom = new TextBox(); private TextBox txtNom = new TextBox(); private Button boutonEnvoyer = new Button("Envoyer au service"); public EtudiantServiceUsageExample() { add(new Label("Prénom et Nom: ")); add(txtPrenom); // modifie add(txtNom); // rajoute add(boutonEnvoyer); add(labelReponseDuService); // Create an asynchronous callback to handle the result. final AsyncCallback callback = new AsyncCallback() { public void onSuccess(Object result) { labelReponseDuService.setText((String)result); } public void onFailure(Throwable caught) { labelReponseDuService.setText("Problème de communication avec le service"); } }; // Listen for the button clicks boutonEnvoyer.addClickListener(new ClickListener(){ public void onClick(Widget w) { getEtudiantService().ajouteEtudiant(txtPrenom.getText(), txtNom.getText(), callback); } }); } /* aucune modification ici */ public static EtudiantServiceAsync getEtudiantService(){ // Create the client proxy. Note that although you are creating the // service interface proper, you cast the result to the asynchronous // version of // the interface. The cast is always safe because the generated proxy // implements the asynchronous interface automatically. EtudiantServiceAsync service = (EtudiantServiceAsync) GWT.create(EtudiantService.class); // Specify the URL at which our service implementation is running. // Note that the target URL must reside on the same domain and port from // which the host page was served. // ServiceDefTarget endpoint = (ServiceDefTarget) service; String moduleRelativeURL = GWT.getModuleBaseURL() + "etudiantservice"; endpoint.setServiceEntryPoint(moduleRelativeURL); return service; } Enfin, nous allons modifier le point d'entrée de l'application, la classe MainEntryPoint.java, afin qu'elle ajoute dans la page html principale une instance de la classe d'exemple d'utilisation ci-dessus (qui est en réalité un élément de GUI, un VerticalPanel) : MainEntry.java : public void onModuleLoad() { RootPanel.get().add(new EtudiantServiceUsageExample()); } On exécute ensuite le projet et on doit obtenir ceci : Et on voit dans firebug l'appel au service et la manière dont GWT encode les paramètres d'envoi par HTTP POST.
|
Powered by MindTouch Deki Open Source Edition v.8.08 |