by Davide Cerbo e Stefano Linguerri
La programmazione web sta facendo passi da gigante e oggi l’utente si aspetta che l’esperienza di utilizzo si avvicini sempre di più a quella a cui è abituato nei classici applicativi desktop. Il mondo degli sviluppatori ha risposto inventanto una nuova sigla: RIA, cioè Rich Internet Application. Google non è stata a guardare e ha fornito la sua risposta a questa esigenza donando alla community Google Web Toolkit. Questo nuovo framework permette di sviluppare in Java tutta l’interfaccia utente per poi ottenere un codice javascript che funzionerà su qualsiasi browser web senza l’installazione di plugin aggiuntivi. In questa presentazione vedremo:
* perchè sviluppare applicazioni RIA
* perchè usare GWT
* come GWT utilizza AJAX per comunicare con il server
* le ottimizzazione che avremo utilizzando GWT
* come uscire dal browser con Google Gear e Mozilla Prism
* e non solo…
17. Moduli *.gwt.xml In GWT è possibile definire dei moduli . Ogni modulo è configurato in un file con estensione: gwt.xml < module > < inherits name ="com.google.gwt.user.User"/> < entry-point class ="com.example.foo.MyModule"/> </ module > Qui definiamo l'entry-point MyModule che sarà il "main" del nostro applicativo
18. Tags *.gwt.xml <inherits name="logical-module-name" /> : Per importare altri moduli <entry-point class="classname" /> : La classe entry point dell'applicazione <source path="path" //> : Definisce in quale package riesodo i sorgenti della parte client, per default il package è client <public path="path"/> : Il percorso dove risiedono le componenti statiche e componenti di terze parti (javascript, html, immagini, etc, etc). <servlet path="url-path" class="classname"/> : Definisce il componente lato server per le chiamate RPC. <script src="js-url"/>,<stylesheet src="css-url"/> : Per includere javascript e css <extend-property name="client-property-name" values="comma-separated-values"/> : Per aggiungere delle proprietà
26. RootPanel rappresenta, nella pagina, il contenitore di più alto livello (body html o un elemento div da noi definito). Per aggiungere elementi a RootPanel è sufficente chiamare: RootPanel.get().add(myPanel);
31. Remote Procedure Call Una Remote Procedure Call è una funzione implementata lato server invocata dal client. GWT RPC NON è la stessa tecnologia su cui sono basati i web services SOAP o REST .
32. RPC gwt.xml Nel nostro file gwt.xml dovremmo configurare la classe che implementa il servizio lato server. Dobbiamo mappare inoltre anche la path alla quale tale implementazione risponderà ... <servlet path=" /countVisitors " class=" it.jk.server.VisitorsCountServiceImpl "/> ...
37. RPC Server package it.jk.server; import com.google.gwt.user.client.rpc.RemoteServiceRelativePath; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import it.jk.client.VisitorsCountService; @RemoteServiceRelativePath("countVisitors") public class VisitorsCountServiceImpl extends RemoteServiceServlet implements VisitorsCountService { public String getVisitors (String name) { VISITORS++; return "Ciao " + name + " è il click numero " + VISITORS; } private static int VISITORS = 0; }
38. RPC Client private void getVisitorInfo(String userName) { // Iniziallizza il proxy per il servizio if (visitorCountService == null) { visitorCountService = GWT.create(VisitorsCountService.class) ; } // Prepara l'ogetto per la callback AsyncCallback <String> callback = new AsyncCallback <String>() { public void onFailure(Throwable caught) { // TODO: Gestione errore } public void onSuccess(String result) { salutoLabel.setText(result); } }; // Chiama il metodo per sapere quante visite ci sono state visitorCountService.getVisitors(userName, callback); }
44. Flickr Mash-up Miliardi di informazioni sparse per la rete Fare un applicativo Mash-up vuol dire sviluppare un software capace di aggregarle in un solo punto
45. Ringraziamo le API... ...e vediamo quelle che usa Flickr http://www.flickr.com/services/api/ REST + JSON
46. Prima prova di contatto... String url = " http://www.flickr.com/services/rest/?format=json& method =flickr.interestingness.getList& api_key =a1234567a1ce1bd1aba1234 aaec123e1& user_id =1234567@N01 "; RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, apiUrl); Request req = builder.sendRequest(null, new JsonRequestCallBack()); public class JsonRequestCallBack implements RequestCallback { public void onError(Request request, Throwable exception) { //gestisco richiesta in errore } public void onResponseReceived(Request req, Response response) { //gestisco richiesta avvenuta con successo } }
47. ...ma non funziona AJAX non permette richieste CROSS-SITE Questo a causa del principio di sicurezza SOP (same origin policy) Abbiamo 3 soluzioni: 1) configurare un alias dell'host remoto in modo da poterlo chiamare come se fosse localhost 2) Creare un componente server-side che faccia da proxy 3) Imbrogliare : l'unica risorsa che possiamo richiedere esternamente è Javascript...uhm...interessante
48.
49. myCallbackFunction ({"photos": {"page":1, "pages":5, "perpage":100, "total":500, ...) myCallbackFunction sarà il nome di una funzione Javascript a cui sarà passato l'oggetto JSON ritornato da Flickr L'url è diventato: String url = " http://.../rest/?format=json& method =...& api_key =a...1& user_id =1234567@N01& jsoncallback = myCallbackFunction ";
50.
51. GWT rinomina i metodi per ridurre le dimensioni del js Soluzione Con JSNI definiamo codice Javascript in una una classe public native static void getJson(String callback , String url, MyHandler h ) /*-{ var script = document.createElement("script"); script.setAttribute("src", url+ callback ); script.setAttribute("type", "text/javascript"); window[callback] = function(jsonObj){ h .@ it.pronetics.MyHandler::handle (Lcom/google/gwt/core/client/JavaScriptObject;)( obj ); window[callback + "done"] = true; } document.body.appendChild(script); }-*/;
52. Ancora J ava S cript N ative I nterface package it.pronetics; ... public class MyHandler { public void handle(JavaScriptObject javaScriptObject) { //creiamo l'oggetto JSON JSONObject object = new JSONObject(javaScriptObject); // isObject() e isArray() ritornano null in caso di errore, // il valore recuperato in caso di successo JSONArray photos = object.get( "photos" ).isObject().get( "photo" ).isArray(); String thumbnail = buildPhotoUrl(photoJson, "s"); .... Image image = new Image(thumbnail); gallery.setWidget(row, column, image); } ... bla bla bla ... }
54. Il mio widget public class ImageSlide extends Composite { //estendere la classe Composite come consigliato da GWT public ImageSlide(String thumbnail, String preview, final String link) { VerticalPanel mainPanel = new VerticalPanel(); HorizontalPanel details = new HorizontalPanel(); Image thumbnailImage = new Image(thumbnail); mainPanel.add(thumbnailImage); mainPanel.add(details); mainPanel.setBorderWidth(1); Button show = new Button("Show"); Button open = new Button("Open"); details.add(show); details.add(open); final PopupPanel popup = new PopupPanel(); Image previewImage = new Image(preview); VerticalPanel imagePanel = new VerticalPanel(); imagePanel.add(previewImage); previewImage.addClickListener(new ClickListener(){ public void onClick(Widget sender) { popup.hide(); } }); popup.setWidget(imagePanel); show.addClickListener(new ClickListener(){ public void onClick(Widget sender) { popup.show(); } }); open.addClickListener(new ClickListener(){ public void onClick(Widget sender) { Window.open(link, "_blank","enabled");}}); initWidget(mainPanel); //chiamata obbligatoria! } }
58. ...ma come abbiamo fatto? public ImageSlide(String thumbnail, String preview, String title, final String link) { ... creo vari panel ... Image thumbnailImage = new Image(thumbnail); thumbnailImage.setStyleName("image-panel"); mainPanel.add(thumbnailImage); mainPanel.setCellHorizontalAlignment(thumbnailImage, HasHorizontalAlignment.ALIGN_CENTER); mainPanel.add(details); Button open = new Button("View on Flickr", new OpenLinkOnClick(link)); open.setWidth("100%"); Button close = new Button("Close me!", new ClosePopupPanelOnClick(popup)); close.setWidth("100%"); Image previewImage = new Image(preview); VerticalPanel imagePanel = new VerticalPanel(); imagePanel.add(previewImage); infoPanel.add(new Label(title, true)); infoPanel.add(open); infoPanel.add(close); previewTab.add(imagePanel, "Image"); previewTab.add(infoPanel, "Get Info"); previewTab.selectTab(0); previewTab.setAnimationEnabled(true); previewImage.addClickListener(new ClosePopupPanelOnClick(popup)); thumbnailImage.addClickListener(new DisplayPopupOnClick(popup)); initWidget(mainPanel); //chiamata obbligatoria! }
59. Ma soprattutto CSS E' stato modificato il file FlickrGWT.css .image-panel { background: url('img/bg.png') no-repeat top center; padding: 0px 10px 10px 10px; } Ricordate : GWT in fondo crea semplici pagine HTML i cui stili possono essere impostati con semplici CSS
60.
61. Specifichiamo l'id del div che conterrà la nostra applicazione: RootPanel.get("flickr-gallery").add(gallery);
66. L'utilizzo è ancora più semplice ToolBarImage bundle = (ToolBarImage)GWT.create(ToolBarImage. class ); Image save = bundle.save().createImage(); toolbar.add(save);