View
3.929
Download
2
Category
Preview:
DESCRIPTION
Présentation sur Ajax en Java dans le cadre du cours combiné GTI780 / MTI780, Sujets spéciaux en TI, donné par Claude Coulombe, à l\'Ecole de technologie supérieure, Montréal, Automne 2008
Citation preview
Ajax en JavaAjax en Java
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Sujets spéciaux en TI
Concepts et outils
École de technologie supérieurepar
Claude Coulombe
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Applications Web en Java - rappelApplications Web en Java - rappel
* Source : Sun http://java.sun.com/javaee/5/docs/tutorial/doc/bnall.html
3 tiers : présentation, application, base de données3 tiers : présentation, application, base de données
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
On peut construire des applications web On peut construire des applications web
simples en Java sans JEE simples en Java sans JEE
Servlets Java (HttpServlet)Servlets Java (HttpServlet)
Java Server Pages (JSP)Java Server Pages (JSP)
Simple serveur de pages et conteneur Simple serveur de pages et conteneur
de servlets comme Tomcatde servlets comme Tomcat
GET et POSTGET et POST
EL* & JSTL**EL* & JSTL**
Applications Web en Java - rappelApplications Web en Java - rappel
* EL: Unified Expresion Language ** JSTL: Java Server Tal Library
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
AjaxAjax – Une véritable percée! – Une véritable percée!
AJAXAJAX
Le premier à utiliser le terme AJAXfut Jesse James Garrett en février 2005
* Source Clipart : http://www.clipart.com
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Ajax (Asynchronous JavaScript & XML)Ajax (Asynchronous JavaScript & XML)
Fini le pénible rechargement de pages!Fini le pénible rechargement de pages!
Réalise des requêtes asynchrones au serveur Réalise des requêtes asynchrones au serveur et fait la mise-à-jour de la page Web sans faire et fait la mise-à-jour de la page Web sans faire de chargement completde chargement complet
Applications Web plus réactives et plus Applications Web plus réactives et plus dynamiquesdynamiques
Objet XMLHttpRequest inventé par M$Objet XMLHttpRequest inventé par M$
Basé sur du code-client en JavaScriptBasé sur du code-client en JavaScript
AjaxAjax – Une véritable percée! – Une véritable percée!
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Fureteur client Interface utilisateur
Moteur Ajax
HTML + CSSAppel JavaScript
Ajax – Ajax – Diagramme de collaborationDiagramme de collaboration
Source : J.J. Garrett 2005 - http://www.adaptivepath.com/ideas/essays/archives/000385.php
Serveur
Base de données, systèmes de gestion
Serveur Web
Requête HTTP
Réponse HTTP(HTML + CSS)
Application Web traditionnelleServeur
Base de données, systèmes de gestion
Serveur Web et XML / JSON/ Texte
Requête HTTP
RéponseXML / JSON / Texte
Application Web Ajax
Fureteur client Interface utilisateur
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Ajax – Diagramme de séquence - Web traditionnelAjax – Diagramme de séquence - Web traditionnel
Source : J.J. Garrett 2005 - http://www.adaptivepath.com/ideas/essays/archives/000385.php
En mode synchrone, le fureteur est gelé en attendant la réponse du serveur.
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Ajax – Diagramme de séquence – Application Web AjaxAjax – Diagramme de séquence – Application Web Ajax
Source : J.J. Garrett 2005 - http://www.adaptivepath.com/ideas/essays/archives/000385.php
En mode asynchrone, l'exécution dans le fureteur sur le poste client se poursuit sans attendre la réponse du serveur. La réponse sera traitée par une fonction de retour (fonction Callback) quand elle arrivera. L'état de la requête est donné par l'attribut readyState de l'objet XMLHttpRequest.
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
CanevasCanevas* * d'applications Java & extensions Ajaxd'applications Java & extensions Ajax
* Note : traduction de Framework également traduit par cadre d'applications ou cadriciel
Echo Cocoon Millstone OXFStruts SOFIA Tapestry WebWorkRIFE Spring MVC Canyamo Maverick JPublish JATO Folium Jucas Verge Niggle Bishop BarracudaAction Framework Shocks TeaServlet WingS Expresso Bento jStatemachine Jzonic OpenEmcee Turbine Scope WarfareJWAA Jaffa Jacquard MacawSmile MyFaces Chiba JBananaJeenius JWarp Genie MelatiDovetail Cameleon JFormular Xoplon Japple Helma Dinamica WebOnSwingNacho Cassandra Baritus Stripes Click GWT
Spring Spring MVC
JSF Facelets Struts 1 Struts 2 Tapestry WebWork
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Canevas d'applications Web de 1ère génération, à base de Canevas d'applications Web de 1ère génération, à base de Servlet/JSP, (2000)Servlet/JSP, (2000)
Centré serveurCentré serveur
Code source libreCode source libre
Patron MVC (Modèle-Vue-Contrôleur), à base d'actionsPatron MVC (Modèle-Vue-Contrôleur), à base d'actions
Concept de la servlet contrôleur frontal (gère des événements et Concept de la servlet contrôleur frontal (gère des événements et déclenche des actions), les vues étant des pages JSP, le Modèle déclenche des actions), les vues étant des pages JSP, le Modèle une BDune BD
Pas de composantsPas de composants
Encore très populaireEncore très populaire
Support Ajax passe par DWR ou une bibliothèque d'étiquettes Support Ajax passe par DWR ou une bibliothèque d'étiquettes comme Struts-Layoutcomme Struts-Layout
On « enveloppe » un bout de code JavaScript/Ajax pour en faire On « enveloppe » un bout de code JavaScript/Ajax pour en faire une étiquette (tag) JSPune étiquette (tag) JSP
http://struts.apache.org/http://struts.apache.org/
Struts et AjaxStruts et Ajax
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
JSF est une technologie du standard JEE (Java Enterprise Edition) JSF est une technologie du standard JEE (Java Enterprise Edition) s'appuyant sur JSP* et une bibliothèque de composants s'appuyant sur JSP* et une bibliothèque de composants d'interface (étiquettes ou tags JSF), (2004)d'interface (étiquettes ou tags JSF), (2004)
Centré serveur, gère l'état des composants sur le serveur Centré serveur, gère l'état des composants sur le serveur
Patron MVCPatron MVC
Code source libreCode source libre
JSR 127, JSR 252, JSR 314JSR 127, JSR 252, JSR 314
Composants Ajax intégrées à JSFComposants Ajax intégrées à JSF
En général, on « enveloppe » un composant JavaScript pour en En général, on « enveloppe » un composant JavaScript pour en faire une étiquette (tag) JSFfaire une étiquette (tag) JSF
Par exemple, une étiquette JSF placera dans la page HTML un Par exemple, une étiquette JSF placera dans la page HTML un menu déroulant et un code JavaScript qui appellera le serveur de menu déroulant et un code JavaScript qui appellera le serveur de façon asynchrone pour une mise à jour dynamique.façon asynchrone pour une mise à jour dynamique.
http://java.sun.com/javaee/javaserverfaces/http://java.sun.com/javaee/javaserverfaces/
JavaServer Faces (JSF) et AjaxJavaServer Faces (JSF) et Ajax
* Note : aussi Facelet
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
JSF et Ajax – JSF et Ajax – Différents fournisseursDifférents fournisseurs
Source : Jboss http://www.jboss.org/jbossrichfaces/
JBoss RichFaces (anciennement ajax4jsf) http://www.jboss.org/jbossrichfaces/
ICEFaces http://www.icefaces.org/main/home/index.jsp
Oracle ADF http://www.oracle.com/technology/products/adf/adffaces/index.html
Comparaison : http://www.jsfmatrix.net/
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Exemple d'intégration d'une composante Ajax avec RichFacesExemple d'intégration d'une composante Ajax avec RichFaces
JBoss Seam est un canevas d'applications Web qui intègre Ajax, JBoss Seam est un canevas d'applications Web qui intègre Ajax, JSF, EJB3 (Enterprise Java Beans) et les PortletsJSF, EJB3 (Enterprise Java Beans) et les Portlets
http://www.jboss.com/products/seamhttp://www.jboss.com/products/seam
JSF et Ajax – JBoss RichFacesJSF et Ajax – JBoss RichFaces
Source : Jboss http://www.jboss.org/jbossrichfaces/
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Canevas d'applications Web en Java avec des Canevas d'applications Web en Java avec des extensions Ajax, (2001)extensions Ajax, (2001)
Inspiré de WebObjects d'AppleInspiré de WebObjects d'Apple
Centré serveur, Tapestry gère l'état des Centré serveur, Tapestry gère l'état des composants sur le serveur (~JSF)composants sur le serveur (~JSF)
Patron MVCPatron MVC
Code source libreCode source libre
Langage ONGL Langage ONGL (Object-Graph Navigation Language)(Object-Graph Navigation Language)
Support Ajax via composants qui enveloppent Support Ajax via composants qui enveloppent du JavaScript Prototype & Scriptaculousdu JavaScript Prototype & Scriptaculous
http://tapestry.apache.org/http://tapestry.apache.org/
TapestryTapestry
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Canevas d'applications Web en Java avec des Canevas d'applications Web en Java avec des extensions Ajax (~ JSF et Tapestry), (2005)extensions Ajax (~ JSF et Tapestry), (2005)
Centré serveur, Wicket gère l'état des composants Centré serveur, Wicket gère l'état des composants sur le serveursur le serveur
Patron MVCPatron MVC
Code source libreCode source libre
API semblable à SWING (~GWT)API semblable à SWING (~GWT)
Bonne séparation de la présentation (pages en Bonne séparation de la présentation (pages en XHTML) et de la logique (requête POSTXHTML) et de la logique (requête POST
Une page XHTML est liée à un composant Java et Une page XHTML est liée à un composant Java et à un modèle POJO (Plain Old Java Object) à un modèle POJO (Plain Old Java Object)
http://wicket.apache.org/http://wicket.apache.org/
WicketWicket
Source : http://wicketstuff.org
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
WicketWicket
// Un exemple de champ de texte avec suggestionsAutoCompleteTextField monChamp = new AutoCompleteTextField("Tapez", new Model("")){ protected Iterator getChoices(String entree) { return monService.getPossibleChoices(entree); } };
Source : http://wicketstuff.org
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
WicketWicket
// Ici, deux menus déroulants qui sont dépendantsprivate static void linkDropDownChoices(DropDownChoice menu1, final DropDownChoice menu2){ // s'assurer que le menu déroulant menu2 affichera son attribut Id menu2.setOutputMarkupId(true); // attribuer une fonction Callback réagissant à l'événement onchange sur le menu déroulant menu1 menu1.add(new AjaxFormComponentUpdatingBehavior("onchange") { protected void onUpdate(AjaxRequestTarget cible) { // lorsque l'événement onchange est déclenché pour menu1, actualiser le contenu de menu2 cible.addComponent(menu2); } }); } Source : http://wicketstuff.org
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Canevas d'applications Web en Java (2005)Canevas d'applications Web en Java (2005)
Centré serveur mais comporte un terminal graphique Centré serveur mais comporte un terminal graphique relativement passif du côté clientrelativement passif du côté client
Code source libreCode source libre
Inspiré de SWINGInspiré de SWING
Ajax sans écrire de JavaScriptAjax sans écrire de JavaScript
Echo2 génère une application Java dont le code est délivré Echo2 génère une application Java dont le code est délivré par des Servlets sur le clientpar des Servlets sur le client
Le client contient un exécutable JavaScript chargé par le Le client contient un exécutable JavaScript chargé par le fureteur. L'exécutable communique (HTTP et Ajax) avec le fureteur. L'exécutable communique (HTTP et Ajax) avec le serveur J2EE qui exécute une application Java.serveur J2EE qui exécute une application Java.
Application riche en une seule pageApplication riche en une seule page
http://echo.nextapp.com/site/http://echo.nextapp.com/site/
Echo 2Echo 2
Source: http://echo.nextapp.com/site/
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
ZK est un canevas d'applications Ajax en JavaZK est un canevas d'applications Ajax en Java
Ajax sans écrire de JavaScript Ajax sans écrire de JavaScript
Code source libre, licence GPL 2Code source libre, licence GPL 2
Bibliothèque de composants ~ SWINGBibliothèque de composants ~ SWING
Moins de programmation car il propose un Moins de programmation car il propose un langage déclaratif de description des interfaces langage déclaratif de description des interfaces basé sur XML (en fait XUL)basé sur XML (en fait XUL)
Centré serveurCentré serveur
http://www.zkoss.org/http://www.zkoss.org/
ZKZK
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Java Applet Java Applet Lourde, exige JVMLourde, exige JVM
Nouvelle applet Java 6 version, plus légère et rapideNouvelle applet Java 6 version, plus légère et rapide
Réputation de lourdeur et de lenteur difficile à contrerRéputation de lourdeur et de lenteur difficile à contrer
JavaFXJavaFXTrop tôt pour se prononcer (2008)Trop tôt pour se prononcer (2008)
Exige JVM, langage à scriptExige JVM, langage à script
http://www.javafx.com/http://www.javafx.com/
Java Applet & JavaFXJava Applet & JavaFX
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Canevas d'applications Web en Java (2002)Canevas d'applications Web en Java (2002)
En code source libre, licence GPLEn code source libre, licence GPL
Langage déclaratif LZX de description des Langage déclaratif LZX de description des interfaces basé sur XMLinterfaces basé sur XML
Serveur OpenLaszlo basé sur une servlet Java Serveur OpenLaszlo basé sur une servlet Java qui compile l'application écrite en LZX vers un qui compile l'application écrite en LZX vers un exécutableexécutable
Client basé sur le plugiciel Flash d'AdobeClient basé sur le plugiciel Flash d'Adobe
http://www.openlaszlo.org/http://www.openlaszlo.org/
OpenLaszloOpenLaszlo
Source: http://www.openlaszlo.org
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
jMaki « enveloppe » un composant JavaScript jMaki « enveloppe » un composant JavaScript pour en faire une balise (tag) JSP ou JSFpour en faire une balise (tag) JSP ou JSF
Compatible avec Dojo, Scriptaculous, YUICompatible avec Dojo, Scriptaculous, YUI
http://jmaki.com/http://jmaki.com/
jMakijMaki
* Source image: http://jmaki.com
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Boîte à outils : client (JavaScript) & serveur Boîte à outils : client (JavaScript) & serveur (servlet Java DWRServlet), (2004)(servlet Java DWRServlet), (2004)
Code source libreCode source libre
Communication RMI (Remote Method Invocation), Communication RMI (Remote Method Invocation), entre des classes Java et des classes JavaScript entre des classes Java et des classes JavaScript miroirs qui sont générées et ajout de Callbacksmiroirs qui sont générées et ajout de Callbacks
Petit fichier de configuration dwr.xmlPetit fichier de configuration dwr.xml
Capable de remplacer plusieurs servlets (i.e. Capable de remplacer plusieurs servlets (i.e. plusieurs URLs) en utilisant une structure de liste plusieurs URLs) en utilisant une structure de liste pour des échanges groupéspour des échanges groupés
http://directwebremoting.org/http://directwebremoting.org/
DWR (Direct Web Remoting)DWR (Direct Web Remoting)
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Création d'applications Ajax sans écrire de Création d'applications Ajax sans écrire de JavaScript, (2006)JavaScript, (2006)
Centré clientCentré client
Code source libreCode source libre
Réalisé en Java le code client est compilé en Réalisé en Java le code client est compilé en JavaScriptJavaScript
L'application cliente peut être déployée sur L'application cliente peut être déployée sur n’importe quel serveur Web comme Apachen’importe quel serveur Web comme Apache
Le serveur peut être basé sur des servlets Java Le serveur peut être basé sur des servlets Java ou tout autre technologie ou tout autre technologie (PHP, Rails, Perl, .Net, …)(PHP, Rails, Perl, .Net, …)
http://code.google.com/webtoolkit/http://code.google.com/webtoolkit/
GWT (Google Web Toolkit)GWT (Google Web Toolkit)
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Java – Java – Avantages & inconvénientsAvantages & inconvénients
* Source Clipart : http://www.clipart.com
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Java - AvantagesJava - Avantages “Write Once, Run Anywhere” (Windows, Linux, Mac OS X, Unix) Maintenant en code source libre Le langage du génie logiciel Le royaume des patrons de conception Le plus grand nombre de bibliothèques dans tous les domaines Le plus grand nombre de canevas d'applications Le plus grand nombre d'outils de toute sorte Véritable langage de POO, langage à typage statique Langage mature, sécuritaire et performant Utilise de puissants EDI* comme Eclipse, Netbeans ou IntelliJ La plus importante communauté de développeurs Un nombre impressionnant de livres et une abondante documentation sur le Web
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Java - InconvénientsJava - Inconvénients Réputation de technologie compliquée et difficile à mettre en oeuvre. Tendance à surcomplexifier, surdimensionner, à faire des « usines à
gaz », « disproportionnées », à concevoir pour résoudre tous les problèmes. En anglais : over-sized, over-kill, over-featured, over-engineered, over-complicated. Par exemple, JEE (Java Enterprise Edition) conçu pour de très
grosses applications distribuées sur plusieurs machines a souffert de cette tendance (EJB 2, SOAP) Or une bonne ingénierie aboutit à une conception simple, élégante et
adaptable aux besoins. Il faut savoir se limiter, rester modeste. C'est ce qui explique le succès de PHP, RoR et des outils logiciels
Google et l'inspiration qu'ils apportent à EJB3 et JEE 6
* Autres exemples : X Window, Corba
« Pourquoi faire simple quand on peut faire compliqué! »
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Choix des technologies Ajax en JavaChoix des technologies Ajax en Java
* Source Clipart : http://www.clipart.com
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Technologies centrées serveur versus centrées clientTechnologies centrées serveur versus centrées client
(server-centric vs client-centric)(server-centric vs client-centric) Code source libre versus technologie propriétaireCode source libre versus technologie propriétaire Licence libre : Apache, GPL, MIT, etc.Licence libre : Apache, GPL, MIT, etc. 100 % Java versus mixte, i.e. nécessitant de la programmation 100 % Java versus mixte, i.e. nécessitant de la programmation
en JavaScripten JavaScript Richesse de la bibliothèqueRichesse de la bibliothèque Disponibilités d'outils, d'EDIsDisponibilités d'outils, d'EDIs Facilité de programmation, productivitéFacilité de programmation, productivité Endossement par des joueurs importantsEndossement par des joueurs importants Support techniqueSupport technique DocumentationDocumentation Taille et dynamisme de la communautéTaille et dynamisme de la communauté
Autres critères Autres critères
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Struts et Ajax - ConclusionStruts et Ajax - Conclusion
Struts 1.3
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Technologie complexe (ci-dessous le cycle de vie d'une composant JSF)Technologie complexe (ci-dessous le cycle de vie d'une composant JSF)
Difficultés si on mélange des composants de différents fournisseursDifficultés si on mélange des composants de différents fournisseurs
Difficiles de créer de nouveaux composants « sur mesure »Difficiles de créer de nouveaux composants « sur mesure »
Centré serveurCentré serveur
La bibliothèque de composants de base est insuffisante pour réaliser des La bibliothèque de composants de base est insuffisante pour réaliser des applications d'entreprise. Il faut se procurer des bibliothèques chez applications d'entreprise. Il faut se procurer des bibliothèques chez différents fournisseurs.différents fournisseurs.
À mon avis le meilleur choix d'outils pour combiner JSF et Ajax est JBoss À mon avis le meilleur choix d'outils pour combiner JSF et Ajax est JBoss Seam avec RichFacesSeam avec RichFaces
JSF et Ajax – ConclusionJSF et Ajax – Conclusion
Source image : Sun http://java.sun.com/javaee/5/docs/tutorial/doc/bnaqq.html
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Les canevas d'applications Web basé sur JSP ont Les canevas d'applications Web basé sur JSP ont l’inconvénient majeur de nécessiter une bonne l’inconvénient majeur de nécessiter une bonne connaissance de JavaScript pour pouvoir développer connaissance de JavaScript pour pouvoir développer en Ajax. en Ajax.
Normal, ils sont apparus avant Ajax (2005) et l'ajout Normal, ils sont apparus avant Ajax (2005) et l'ajout de fonctionalités Ajax est souvent une rustine (patch)de fonctionalités Ajax est souvent une rustine (patch)
Si vous utilisez déjà un canevas d'applications et qu'il Si vous utilisez déjà un canevas d'applications et qu'il possède une extension Ajax alors continuez avec ces possède une extension Ajax alors continuez avec ces outils.outils.
SVP, évitez de « réinventer la roue »* en créant votre SVP, évitez de « réinventer la roue »* en créant votre propre canevas d'application! propre canevas d'application!
CanevasCanevas d'applications Java & Ajax - Conclusiond'applications Java & Ajax - Conclusion
* Note : en anglais NIH : pour Not Invented Here , le cancer du développement logiciel
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
GWT – « Le prochain gros truc! » GWT – « Le prochain gros truc! »
GWTGWT
* Source Clipart : http://www.clipart.com
Montréal, novembre 2008Montréal, novembre 2008GTI-780 / MTI-780GTI-780 / MTI-780
Ressources - LivresRessources - LivresAjax on Javapar Steven Olson, Steve Oualline228 pagesO'Reilly Media, Inc.(22 février, 2007) www.oreilly.com/catalog/9780596101879/#top
Recommended