applicazioni internet

Transcript

applicazioni internet
applicazioni internet
[email protected]
http://creativecommons.org/licenses/by-nc-sa/3.0/it/
1. introduzione alle tecnologie
Evoluzione del web
Nel web 1.0 il server era il nucleo della rete. Il tempo di reazione passa così dalle settimane/mesi del web tradizionale ai giorni/minuti del web 2.0, fino al tempo reale del web
squared.
Nel web squared, inoltre, si fanno giungere messaggi all’utente per far iniziare l’attività:
enormi potenzialità di marketing.
Ombra informativa: tutto quanto non detto, ma che posso dedurre da una serie di segnali deboli e convergenti.
Requisiti generali delle applicazioni
• Facilità d’uso
• Dati ben presentati e ben indicizzati
• Alta affidabilità
• Sicurezza
◦ Da progettare subito
• Scalabilità
Architetture a tre strati
Un buon modo di procedere è l’approccio stratificato, in cui si divide la complessità del
problema in una serie di sottomoduli. La presenza di vincoli non funzionali (= costi, sicurezza, scalabilità, flessibilità) non consente di vedere ogni strato come blackbox.
1. Client tier: strato di presentazione
◦ Trasforma informazioni digitali in qualcosa di usabile dalle persone
2. Middle tier: strato di business logic
◦ Esegue la logica di business e le politiche aziendali
◦ Il server nasconde la complessità distribuita all’utente finale
◦ Progettabile in sottolayer per migliorare la flessibilità
3. Data access tier: strato dei dati
◦ Ospita l’insieme dei dati che si vogliono manipolare nell’applicazione
◦ Garantisce la coerenza e la sicurezza delle informazioni
◦ Gestisce lock e transazioni
Applicazioni web
Si definisce applicazione web un modello a 3-4 strati in cui un insieme di utenti che usano un opportuno software di presentazione interagiscono con un server applicativo utilizzando il protocollo HTTP.
Un’applicazione web è un sistema con diverse caratteristiche:
• distribuito
• asincrono
◦ le richieste possono arrivare al server in qualunque momento
• basato su dati semi-strutturati
◦ interazione basata perlopiù sullo scambio di messaggi testuali
• senza ordinamento intrinseco
◦ un client può inviare una sequenza arbitraria di richieste
• multiutente
◦ più persone possono accedere allo stesso servizio nello stesso momento
•
non sicuro
◦ di base, HTTP non ha un sistema di sicurezza
Non è possibile stabilire a priori quale azione verrà effettuata dall’utente, quindi nella
reazione devo capire che richiesta mi è giunta, decodificarla, aggiornare lo stato e costruire una risposta.
✔
✔
✘
✘
Accesso universale
Facilità d’uso e di implementazione
Creazione di servizi scalabili non così banale
Protocollo legato a un’ottica richiesta-risposta
Operazioni e requisiti lato server
• Validazione dei dati inseriti
• Implementazione della logica
• Persistenza dei dati
•
•
•
•
Garantire alte prestazioni
◦ Bilanciamento del carico
◦ ...
Semplificare la gestione del sistema
◦ Aggiornamento e configurazione dinamica
◦ …
Mantenere i costi di sviluppo bassi
◦ Generazione automatica di codice...
Sicurezza
◦ Autenticazione
◦ ...
Requisiti lato client
• Accessibilità
• Universalità
Requisiti lato rete
• Evitare la configurazione di dispositivi
• Ridurre il consumo di banda
• Rendere il sistema compatibile con le misure di sicurezza esistenti
• Essere sicuri che il sistema non introduca rischi nel sistema informativo
###
2. il protocollo http
HTTP: caratteristiche base
Protocollo applicativo che si appoggia su un protocollo di trasporto (TCP). Agnostico rispetto al contenuto trasportato.
Un client invia una richiesta, un server risponde con una risposta. Pensato per essere
privo di stati: grossa semplificazione.
Richieste e risposte sono due blocchi di dati, inviati consecutivamente.
• Intestazione, obbligatoria
• Corpo del messaggio, opzionale
Risorse
Una risorsa può essere:
• un documento statico
• un servizio in esecuzione
La stessa risorsa può essere resa disponibile dal server in una pluralità di formati.
Una risorsa è l’oggetto di una transazione HTTP. Per specificare una risorsa si usa l’URL:
schema
Protocollo con
cui interagire
:// hostname
[:port]
Indirizzo in cui il server si trova in
attesa di connessioni
/ path
Posizione relativa, non
necessariamente reale
Richieste
Indicano una risorsa e quale azione si vuole compiere su di essa.
GET /index.html HTTP 1.1
Host: localhost:8000
Accept: text/html
← Azione su una risorsa
← Sottointestazioni
← Riga vuota
...
← Eventuale corpo
Proprietà delle azioni
• Azioni safe
◦ Non generano cambiamenti nello stato interno del server
• Azioni idempotenti
◦ L’effetto di più richieste identiche è lo stesso di quello di una sola richiesta
Le azioni possono essere combinate per modellare la semantica CRUD.
Azioni principali
• GET [S & I] [R]
◦ Può essere condizionale o parziale. Non ha corpo.
• HEAD [S & I]
◦ Simile a una GET, ma ci si scambia solo le intestazioni.
• POST [!S & !I] [U]
◦ Invio risorse e chiedo il documento risultante.
•
•
PUT [!S & I] [C]
◦ Voglio creare una risorsa sul server.
DELETE [!S & I] [D]
◦ Distruggi la risorsa indicata.
Risposte
Rappresentano il risultato della richiesta.
HTTP/1.1 200 OK
Content-Type: text/html
Date: Sat 01 Mar 2014, 08:22:22 GMT
← Codice di stato
← Sottointestazioni
← Riga vuota
...
← Eventuale corpo
Codici di stato
• 1XX, informative
◦ Messaggi inviati a titolo informativo mentre la richiesta è ancora svolta
• 2XX, successo
• 3XX, redirezione
◦ Il client deve eseguire ulteriori azioni per arrivare al risultato
• 4XX, client error
◦ Non si può accontentare la richiesta incorretta del client
• 5XX, server error
◦ Il server non può soddisfare la richiesta per un problema interno
Intestazioni
• Intestazioni generali
◦ Indicano proprietà generali del messaggio
▪ Cache-Control
▪ Transfer-Encoding
▪ …
• Intestazioni relative ad un’entità
◦ Forniscono informazioni sul corpo del messaggio o sulla URL
▪ Content-Type
▪ Last-Modified
▪ ...
• Intestazioni relative alla richiesta
◦ Descrivono la richiesta ed il client al server
▪ Accept
▪ If-Modified-Since
▪ ...
• Intestazioni relative alla risposta
◦ Forniscono ulteriori informazioni sull’esito della richiesta
▪ Location
▪ WWW-Authenticate
▪ …
Corpo del messaggio
Se c’è un corpo, la lunghezza dev’essere nota a priori, per intero o per chunk.
Schemi di interazione
GET
Richieste multiple
POST
Richieste sovrapposte
Autenticazione
HTTP ha meccanismi intrinseci per gestire l’autenticazione.
• Basic Authentication
◦ Si inviano user e password codificate in base 64 al server
◦ Reversibile e quindi non sicura
• Digest Authentication
◦ Si basa sul meccanismo di risoluzione di una sfida
◦ Non usata perché ha tutta una serie di complicazioni implementative
Sessioni
Una sessione serve per riconoscere che un insieme di coppie (richiesta, risposta) non
sono indipendenti tra loro, ma fanno riferimento alla stessa entità. Non ha niente a che
fare con l’autenticazione, in quanto esistono sessioni anonime.
Per gestirle:
• genero un numero su 128 bit
• lo comunico al client
• il client mi invia il numero in tutte le richieste successive
Per comunicare l’ID al client si possono adottare diverse strategie:
• Passaggio esplicito
•
•
•
◦ L’ID di sessione è passato mediante campo (es.: campo hidden in un form)
Passaggio mediante cookies
◦ Il server invia un cookie al client
◦ Il client lo utilizza automaticamente nelle successive richieste
Passaggio mediante riscrittura dei link
◦ I link sono riscritti aggiungendo l’ID della sessione nei vari URL
◦ Utile quando il client non supporta i cookie
Coordinamento delle sessioni dal client
◦ Interessante nei servizi di tipo single page
HTTP lato client
Lavorando in Java si possono usare le librerie del package java.net.*, le cui principali
classi sono URL, URLConnection, HTTPUrlConnection. Quest’ultima è specificata per richieste HTTP (tipo di richiesta, cookie, proxy...) e gestisce automaticamente le richieste
di ridirezione verso altri host.
L’alternativa più semplice, ma un po’ più onerosa a livello di spazio, è l’uso della libreria
HttpClient che dà accesso a tutte le funzionalità del protocollo.
###
3. applicazioni web - java ee
Applicazioni web dinamiche
E’ un’applicazione lato server che crea un’interfaccia – solitamente HTML – per fornire
un servizio agli utenti finali. E’ composta da svariati livelli debolmente interconnessi.
• livello UI
• livello controllo
• livello dati
Comportamento di un’applicazione web
L’applicazione consente all’utente di interagire in maniera controllata con una base dati.
Produce direttamente o indirettamente risultati utili per l’utente. Il livello controllo si occupa di applicare le regole che governano il sistema nel suo complesso.
Logica di un’applicazione web
Le applicazioni web sono discontinue: questo aspetto è fondamentale per permettere ai
server di scalare su un numero molto grande di utenti.
E’ importante che il livello di controllo lato server verifichi la validità dei parametri ricevuti e non faccia affidamento su Javascript. Se i valori sono accettati si salvano da qualche parte: una volta messi in sieme a un po’ di informazioni storiche, si genera una pagina di risposta.
Via via che l’interazione procede, l’applicazione scopre sempre più informazioni circa
quello che vuole l’utente. Grazie ai cookie si può superare la limitazione stateless di
HTTP e correlare la richiesta corrente a quella passata.
Forme di navigazione
• Pagina per pagina
◦ Una nuova richiesta genera una nuova pagina HTML
• Pagina singola
◦ La prima richiesta scarica HTML e JavaScript di gestione
◦ Le richieste successive aprono in background una connessione per ottenere
solo l’essenza dei dati
La navigazione a pagina singola è fonte di alcune criticità:
• Uso dei motori di ricerca
◦ Non si vede il contenuto interno del sito
◦ I motori di ricerca non riescono a indicizzarlo
• Cronologia del browser
◦ Devo farmene carico programmaticamente
• Ciclo di vita
◦ Il primo caricamento è più lento in quanto si scaricano più dati
◦ Bene far apparire subito qualcosa all’utente
Applicazioni JavaEE
Insieme di componenti (= classi) che risiedono su un server e che vengono istanziati
all’interno di un programma contenitore (= programma in esecuzione sul server che monitora le connessioni entranti dalla rete).
Ogni volta che viene ricevuta una richiesta, il contenitore guarda l’URL e istanzia uno o
più oggetti incaricati di elaborare la richiesta, tenendo per sé le funzioni non applicative.
I componenti non possono mai interagire l’uno con l’altro, perché non si istanziano in
modo esplicito e non hanno riferimenti.
Per le nostre applicazioni, il contenitore è il WebContainer. L’utente finale vi dialoga mediante HTTP e al suo interno ha servlet, filtri e JSP.
Contenitore JavaEE
Gestisce il protocollo HTTP e tutte le infrastrutture di sistema per elaborare le richieste e
mantenere i dati di sessione. Può ospitare tante applicazioni web separate tra loro. Nel
corso si userà Tomcat 7.
Servlet
Il componente più elementare. Implementa l’interfaccia Servlet e modella il meccanismo
richiesta/risposta. E’ istanziato dal contenitore che ci fa delle cose, lo tiene vivo e poi
eventualmente lo rilascia.
Interfaccia javax.servlet.Servlet
• init(ServletConfig config)
◦ Inizializzazione del servlet e acquisizione di eventuali risorse globali
• service(ServletRequest req, ServletResponse res)
◦ Invocato tutte le volte che c’è una richiesta
• destroy()
◦ Distruzione del servlet e rilascio delle risorse acquisite
ServletRequest e ServletResponse sono due interfacce di alto livello che modellano una
richiesta ed una risposta, con i vari parametri e il corpo.
Ciclo di vita
1. Il contenitore crea un’unica istanza del servlet e ne invoca il metodo init()
◦ Finchè init() non ritorna, il contenitore non invoca altri metodi del servlet
2. Ad ogni richiesta, il contenitore invoca il metodo service() utilizzando un thread
differente
3. A proprio piacimento, il contenitore decide di invocare il metodo destroy() per rimuovere il servlet
Interfaccia java.servlet.http.HttpServlet
Specializza il concetto di servlet rispetto al trasporto HTTP. Il metodo service() reindirizza
le richieste ricevute sui metodi:
• doGet(HttpServletRequest, Http ServletResponse)
• doPost(...)
• doPut(...)
• doDelete(...)
Possiamo leggere parametri, cookie, header, tipo di richiesta e così via...
HttpServletRequest
Modella i dettagli di una richiesta, fornendo metodi per ispezionarla, aggiungerci informazioni ed eventualmente inoltrarla ad un’altra destinazione.
HttpServletResponse
Contiene tutte le informazioni che descrivono la richiesta corrente. Può essere scritta
byte a byte o come flusso di caratteri. E’ possibile accedere alle intestazioni.
ServletContext
Interfaccia che definisce le interazioni tra servlet e suo contenitore. Modella la VM su cui
sto eseguendo e può essere utilizzato come deposito di informazioni globali a livello applicativo, nonché per identificare il mimetype di un file.
Fornisce il getDispatcher() che serve a inoltrare la richiesta corrente ad un altro componente.
Gestire le sessioni
Si usa l’interfaccia HttpSession che offre metodi per accedere alla sessione, settare, leggere e rimuovere parametri, nonché per invalidare la sessione corrente, rimuovendo tutti i contenuti relativi.
Associare un servlet ad una URL
Si può associare un servlet a una URL in due modi:
• annotazione @WebServlet
• mapping esplicito nel file web.xml
Filtri
Un filtro è un componente, messo a monte dell’esecuzione di un servlet, che trasforma
le richieste inviate a un servlet oppure le risposte che questo genera. Ciascun filtro riceve le richieste destinate a un (insieme di) URL e decide, in base alla propria logica, se
inoltrarle la richiesta al destinatario, modificare uno o più parametri o reindirizzare la richiesta verso un indirizzo differente.
L’associazione filtro-URL è simile a quella servlet-URL. Uno degli ambiti più sensati di utilizzo dei filtri è l’autenticazione.
L’oggetto filtro deve implementare javax.servlet.Filter, che ha i metodi:
• init()
• destroy()
• doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
◦ Il filtraggio vero e proprio.
FilterChain, a sua volta, è un’interfaccia con il solo metodo doFilter(ServletRequest req,
ServletResponse res) che inoltra al prossimo senza sapere chi sia.
Analisi critica
✔ Veloci, efficienti
✔ Scalabili
✔ Flessibili
✘ L’architettura può diventare complessa
✘ Presentazione e logica fortemente accoppiate
JSP
Per superare il problema dell’accoppiamento logica-presentazione, sono nate le JSP (=
Java Server Pages), documenti di testo che a tutti gli effetti sono Servlet. Si possono così
scrivere pagine HTML che contengono pezzi di codice: quando il compilatore elabora la
pagina JSP capisce dove sono i marcatori ed esegue il codice, trattando tutto il resto
come testo.
Più nel dettaglio, si ha che:
1. il contenitore riceve la richiesta della pagina JSP
2. il contenitore identifica il file richiesto e lo trasforma in classi sorgenti Java
3. il compilatore Java compila la classe prodotta
4. la richiesta ricevuta e i relativi parametri vengono inviati al servlet generato
5. il risultato viene inviato al client
Nelle successive richieste viene saltata la fase di compilazione.
In una pagina JSP si possono usare i JavaBean e si hanno 4 variabili predefinite che rappresentano richiesta, risposta, sessione corrente e flusso di caratteri in uscita.
JavaBean
Si definisce JavaBean una classe Java che implementa l’interfaccia Serializable (= è lecito prendere l’oggetto e renderlo persistente). Ha un costruttore pubblico senza argomenti e espone attributi accessibili con metodi setter e getter.
Differenze con i servlet
A livello funzionale, nessuna.
✔ JSP più convenienti quando la risposta contiene principalmente testo
✘ Servlet più convenienti quando la risposta non è testuale e la logica è dominante
###
4. il paradigma mvc - model view controller
Motivazioni e approccio
A differenza dei programmi tradizionali, in cui il flusso di elaborazione è completamente
predeterminato dal programmatore, nel modello web:
• gli ingressi non sono noti sin dall’inizio
• c’è disaccoppiamento totale tra client e server
• non si condividono dati di nessuna natura, se non quelli scambiati
• la sequenza degli eventi non è nota a priori
Occorre dunque modellare cosa è lecito, quando lo è e cosa fare.
Una situazione del genere si presta bene all’uso di una macchina a stati finiti: si presuppone che il nostro server sia in grado di trovarsi in uno e un solo stato del nostro insieme finito. Per ogni coppia (stato iniziale, evento) è indicato lo stato successivo in cui il
sistema si porta e quali azioni occorre svolgere a fronte di tale cambiamento.
Lo stato del sistema è memorizzato in una variabile.
Il paradigma MVC
E’ basato sui concetti di modello, vista e controllore. Quando l’utente fa una richiesta,
questa finisce al contollore che usa la richiesta (dopo averla validata) per modificare il
modello. In base allo stato con cui il modello viene a trovarsi e alla richiesta, si seleziona
quale delle viste dev’essere mostrata.
Modello
Contiene i dati dell’applicazione (quelli che già conosciamo e quelli che scopriremo). Ha
negli attributi i dati che ci riguardano e un insieme di metodi che permettono di aggiornare i dati secondo le regole del gioco.
Occorre pensare alla costruzione thread-safe di modelli, in quanto le operazioni possono
arrivare da thread differenti. E’ implementabile con oggetti Java qualunque.
Vista
Rappresentazione visuale del modello. Trasforma i dati del modello in qualcosa di visibile. A parità di modello si possono ricavare tante viste alternative.
La vista non modifica mai il modello, ma ne legge solo il contenuto.
Controllore
Traduce le richieste provenienti dall’utente in azioni sul modello e decide che vista
dev’essere visualizzata.
Va creato come un servlet o un filtro, fatto in modo che intercetti tutto (= è mappato sul
path /*). Il fatto che ci sia un unico punto di ingresso facilita la validazione dei parametri
e il mantenimento della coerenza.
Modo di procedere
1. Definizione di una macchina a stati
2. Definizione dei parametri associati a ciascuna transazione
3. Selezione delle viste associate a ciascuno stato
4. Implementazione della logica del controller
5. Implementazione delle azioni associate alle transazioni
6. Implementazione delle viste
###
5. uso di tag custom nelle pagine jsp
Tag custom
L’idea di base dietro i tag custom è la seguente: così come il browser esegue del codice
in corrispondenza dei tag HTML, non possiamo fare trasformazioni lato server? Detto in
altri termini, mi piacerebbe che un tag del tipo <itera volte="10">Hello world</itera>
stampi 10 volte “Hello world".
Un tag custom descrive dunque in modo dichiarativo le operazioni compiute. Le liberie
vengono rese disponibili mediante due file separati:
• un .jar con il codice da eseguire
• un .xml con estensione .tld che il contenitore consulta per sapere cosa fare con i
vari tag
✔ Livello di utilizzo molto alto in contesti differenti
✘ All’inizio ci si sforza un po’
Un tag è dunque un elemento XML dotato di un nome, un prefisso ed eventuali parametri.
<mail:send to=“[email protected]" from="[email protected]">Hello Joe!</mail:send>
Quando un qualcosa di questo tipo viene trovato dal page compiler:
• ci si chiede se mail è conosciuto
◦ tendenzialmente sì, perché se voglio usare dei tag faccio riferimento a un .tlb
• si cerca send
• si traduce tutto questo in “creo un’istanza di send e invoco su questo oggetto un
metodo, passandogli gli attributi selezionati"
La classe Java corrispondente ha accesso sia agli attributi passati, che al testo che il tag
racchiude come parametro.
Librerie di tag
Meglio usare librerie di tag custom già pronte. I file .tld sono descritti secondo uno schema XML.
Utilizzo dei tag
Se voglio usare dei tag custom in una pagina, devo:
• dichiarare l’utilizzo della relativa tag library
◦ dichiarazione diretta
▪ specifico il percorso del .tld nel mio progetto
◦ dichiarazione indiretta
▪ mi riferisco al .tld con un URI inventato, mappato in web.xml
• poter accedere all’implementazione di tale libreria
JSTL: JSP Standard Tag Library
In tutti i contenitori web è presente una libreria standard di tag, utilizzabile in quattro diversi ambiti:
• Funzioni di base
◦ Accesso ai parametri (richiesta, sessione...)
◦ Valutazione condizionale
•
•
•
◦ Gestione delle iterazioni
◦ Gestione di URL
Formattazione di contenuti
◦ Gestione di numeri e dati
◦ Internazionalizzazione
Elaborazione documenti XML
◦ Analisi, trasformazione
Accesso a basi dati
◦ Esecuzione istruzioni SQL
▪ meglio non usarle perché non gestiscono bene le transazioni
###
6. il framework apache struts
Caratteristiche generali
Struts è uno dei framework che supporta MVC.
Il modello, le viste e il controllore sono implementate da interfacce e classi:
• Action
• Result
• FilterDispatcher
Il controllore è fatto da Struts previa configurazione di un filtro nel WebContainer. Va
configurato sull’URL /*. Determina l’azione da invocare in base all’URL contenuta nella
richiesta ed al file struts.xml.
Alla ricezione di una richiesta, viene istanziato l’oggetto Action corrispondente ed eseguito il metodo execute(), che restituisce una stringa che sarà utilizzata per determinare
quale vista selezionare.
Chi si fa carico di ricevere le richieste, decidere in base alla URL a quale Action è da girare la richiesta e invocare l’oggetto Action utilizzando execute() è il FilterDispatcher, il
quale in base alla stringa di risposta decide quale vista attivare.
Prima di prendere la richiesta ricevuta e passarla all’execute(), la fa attraversare a un insieme di moduli intermedi detti intercettori. Ognuno di questi moduli si occupa di un
aspetto ed ha il potere di stoppare la richiesta (= non invocare Action).
Flusso di funzionamento
1. Arriva una richiesta
2. La richiesta è presa dal filtro
3. In base alla URI si va a creare un’istanza di SomethingAction vuota
4. La richiesta è passata agli intercettori
5. Eventuali parametri vengono settati nell’oggetto SomethingAction
6. Sull’oggetto popolato si invoca execute()
7. Quando l’esecuzione è terminata si ritorna una stringa informativa
8. Il filtro guarda la stringa e sceglie la vista successiva
Filter Dispatcher
Verifica l’URI in ingresso e determina quale azione invocare e quale classe azione Java
istanziare.
Intercettatori
Alcuni compiti del controllore vengono affidati a sotto-componenti che prendono il nome
di intercettatori. Possono essere aggiunti o rimossi semplicemente modificando il file di
configurazione di Struts, senza ricompilare l’applicazione.
Di default vengono invocati 10 intercettatori: di interesse sono gli intercettatori params
(che setta i parametri), validation (verifica i vincoli) e workflow (se la verifica è fallita genera INPUT e ricarica la pagina precedente).
Azioni
Classi con il metodo execute(), hanno getter accessibili alle viste e setter accessibili agli
intercettatori. Si presti attenzione al fatto che ogni volta che c’è una richiesta, viene costruito un nuovo oggetto: gli attributi sono quindi volatili e se c’è bisogno di salvare in-
formazioni bisogna appoggiarsi a un modello. Si possono accedere ai parametri della richiesta e agli attributi di richiesta, sessione e applicazione, nonché allo stack dei valori
OGNL.
Risultati
Un metodo dell’azione ritorna una stringa che determina quale risultato eseguire. Un risultato ha un nome e un tipo: il nome di default è “success", mentre il tipo di default è
“dispatcher", che ridirige il browser a una pagina .jsp.
Associare URL e azioni
In aggiunta a tutta una serie di convenzioni, i singoli metodi presenti all’interno di una
classe possono essere annotati con @Action(“/Url"): questo fa aggiungere una corrispondenza tra URL indicata e metodo indicato.
OGNL
In MVC, la vista ha il compito di rendere accessibile il modello ed altri oggetti (richiesta,
parametri, sessione...) agli utenti. Per accedere ad essi da una pagina JSP si può utilizzare OGNL.
Per ogni invocazione di azione e prima di execute() viene creato l’oggetto Value Stack,
utilizzato per salvare l’azione e altri oggetti e consultato durante l’elaborazione dagli intercettatori e anche dalla vista per mostrare risultati e altre informazioni.
Tag di Struts
Struts mette a disposizione un insieme di tag dell’interfaccia utente per interagire più
facilmente con i form HTML e con i suoi elementi.
###
7. accesso alle basi dati
Introduzione a JDBC
JDBC è un’interfaccia che permette di unire l’uso di una base dati relazionale con Java.
Non dipende dalla piattaforma di esecuzione Java, né dal DBMS. Si può fare tutto, ma
con un livello di astrazione basso.
API JDBC
Definite diverse interfacce e classi per accedere a un DBMS:
• Connection, crea un canale di comunicazione con la base dati
• Statement, che esegue statement SQL semplici o preparati
• ResultSet, restituiti dopo l’esecuzione degli Statement ed esaminabili a blocchi
• Supporto alle transazioni
Driver JDBC
E’ il pezzo chiave che consente la comunicazione tra la Java Application, che a sua volta
si appoggia a JDBC, e DBMS. Il costruttore del DB rende disponibile il driver, ovvero un
insieme di classi di cui una esposta come punto d’accesso e accessibile mediante il DriverManager.
Non istanziamo mai il Driver in quanto tale, ma in maniera indiretta utilizzando il DriverManager. Quando c’è bisogno di aprire una connessione verso un certo DB, il DriverManager guarderà tutti i driver registrati chiedendo a ciascuno se è in grado di gestirla. Il
primo a rispondere vince e viene selezionato come driver per la connessione.
Il package java.sql
Contiene classi e interfacce necessarie per:
• connettersi al DB
• inviare richieste SQL
• ottenere i risultati delle interrogazioni
• trasformare i tipi SQL in Java
• generare le eccezioni
In particolare:
• DriverManager
◦ Carica i driver ed attiva le connessioni
• Driver
◦ Dialoga con il singolo DB
• Connection
◦ Modella una essione di lavoro
• Statement
◦ Modella una singola istruzione SQL
• ResultSet
◦ Modella il risultato di uno Statement
Quando ho finito di lavorare con Connection, Statement o ResultSet devo effettuare una
close() per liberare correttamente le risorse.
DriverManager
Offre una coppia di metodi per creare una connessione, uno con solo l’URL del DB e
l’altro con incluse le credenziali di autenticazione. Se tutto va bene ci viene restituita
una Connection.
L’operazione è molto onerosa e quindi si preferisce usare un connection pool, che crea
un tot. di connessioni al DB in modo tale che quando qualcuno deve parlare col DB riceva un oggetto già costruito. Il miglioramento delle prestazioni è sensibile.
DriverManager offre un metodo getConnection() che cerca di aprire una connessione
con la sorgente dati indicata. Solitamente si ricorre all’utilizzo di un DataSource per evitare di replicare i dati d’accesso.
Connection
Rappresenta la connesione (sessione) a un DB specifico. Ha un metodo createStatement() che permette la creazione di un oggetto Statement, usato per le interrogazioni.
Statement e PreparedStatement
L’oggetto Statement offre metodi per eseguire query sul DB. E’ meglio utilizzare l’interfaccia derivata PreparedStatement che esegue un’interrogazione SQL precompilata, evitando così SQL injection.
ResultSet
Sia Statement che PreparedStatement restituiscono dei ResultSet che possono essere
esaminati con dei cursori, una riga alla volta.
Eccezioni
Vi sono tanti motivi per cui un’operazione col DBMS fallisce (rete interrotta tra la VM e il
DB, query SQL non parsabile, dati in formato non valido...). Tutte le eccezioni che possono essere scatenate derivano da SQLException, ed è dunque molto meglio gestire delle
RuntimeException in modo da non perdere l’eccezione e non scrivere tante try/catch.
Generalizzare l’accesso alla base dati
Operativamente si sopravvive nel seguente modo: per ogni tipo di tabella si crea una
classe con gli stessi attributi, in modo da fare un parallelo tra tabelle e oggetti Java manipolabili. Per ognuna di queste classi, si definisce anche una classe DAO – Data Access
Object i cui metodi offrono le funzionalità necessarie a creare, reperire, modificare ed
eliminare le singole righe della tabella secondo la semantica CRUD.
Transazioni
Per default, tutti gli Statement sono eseguiti nel contesto di una singola transazione Possiamo cambiare questo comportamento impostando l’autocommit a FALSE.
Chiavi primarie
C’è la possibilità, dopo aver delegato al DBMS la generazione della chiave primaria, di
ottenere il valore generato.
###
8. hibernate
Il problema
JDBC è semplice ma noioso, perché è facile perdersi di vista risorse e la cosa è dannosa
per i database. Sono stati dunque sviluppati strati software che cercano di rendere più
semplice l’accesso alla base dati.
Le cose non sono così banali come sembrano in quanto il meccanismo di relazione a oggetti ha i concetti di ereditarietà e polimorfismo, cosa che non ho in una base dati. Tuttavia è presente il concetto di navigabilità, una “estensione" dei classici puntatori unidirezionali.
Fare DAO, infatti, richiede scrivere un bel po’ di codice ed è origine di bug. Per questo è
nata una gamma di prodotti ORM.
Object Relational Mapping
ORM è un approccio concettuale orientato ai fatti, dove le corrispondenze tra oggetti e
basi dati sono rese esplicite, così come le dipendenze tra gli oggetti stessi. Genera internamente le istruzioni necessarie per accedere alla base dati.
✔
✔
✔
✔
✘
✘
✘
Maggiore produttività e manutenibilità
Lo schema della bae dati si può creare automaticamente
Prestazioni migliori
Astrazione più grande, in quanto si ragiona con un DB generico
Problemi di prestazioni su cancellazioni massive o grandi join
Prestazioni minori rispetto alle stored procedure
L’uso improprio di ORM porta a basi dati mal progettate
Hibernate
ORM per Java. Definisce un suo linguaggio che permette di interrogare la base dati.
Rispetto al classico approccio base di JDBC, dove si vanno a definire oggetti POJO e classi DAO che offrono i metodi CRUD, con Hibernate si definisce l’oggetto POJO e un file di
mapping .hbm.xml tra gli attributi del POJO e quelli del DB. Avendo fatto questo, non mi
appoggio più a un DAO, ma chiamo Hibernate.
Architettura generale
Per ogni entità nella base dati si crea una classe Java corrispondente, da costruire con lo
stesso standard dei JavaBean (costruttore privo di argomenti, getter e setter) e con un
attributo ID, utile per rappresentare l’identità dell’oggetto.
Annotazioni
Per evitare di scrivere il file di mapping, si possono usare una serie di annotazioni come
@Entity, @Table, @Column e così via.
Mappare le proprietà
In generale devo dire qual è la colonna corrispondente per ogni proprietà del mio oggetto. I riferimenti diretti o indiretti ad altre entità sono considerati relazioni.
Mappare le relazioni
Se io contengo una relazione che va da me verso N altri ho, ad esempio, un attributo di
tipo Collection. Siccome nel contesto della programmazione a oggetti le relazioni sono
modellate tramite puntatori monodirezionali, bisogna fare in modo che le modifiche a un
lato della relazione si propaghino sull’altro. Mediante l’annotazione @Cascade siamo in
grado di imporre una politica di propagazione (PERSIST, UPDATE, REMOVE, ALL).
Configurare Hibernate
Nel file hibernate.cfg.xml si forniscono le informazioni necessarie per accedere a una
specifica base dati (driver, URL, user, password, connection pool, classi...). E’ possibile,
mediante la proprietà hbm2ddl.auto, specificare quali azioni devono essere eseguite
all’inizializzazione del programma per allineare la definizione delle entità lato Java con le
tabelle del DBMS. In fase di sviluppo, inoltre, può essere uile mostrare l’SQL eseguito
mediante opportuno settaggio della proprietà show_sql.
Sessioni
Tutte le operazioni che facciamo in Hibernate avvengono nel contesto di sessioni di lavoro. Una Session rappresenta un’unità di lavoro col database, costituisce il punto di accesso ai dati contenuti nel DBMS ed è creata dall’oggetto SessionFactory, che funziona
correttamente in ambito concorrente. Quando chiudo la sesione le modifiche vengono
rese persistenti.
Nell’ambito di una sessione si svolgono una o più transazioni.
Stato di un oggetto
Dal punto di vista della persistenza, un oggetto Java istanza di una classe mappata, può
assumere tre stati differenti:
• Transient
◦ Appena creato con new, l’oggetto non ha legami di parentela
• Persistent
◦ Quando viene associato ad una sessione, l’oggetto diventa persistente
◦ Eventuali modifiche saranno rese persistenti alla chiusura della transazione
• Detached
◦ Dopo la chiusura della transazione l’oggetto perde il legame di persistenza
Hibernate Query Language
HQL è molto simile a SQL, ma opera sulle classi Java. Supporta molte cose tipiche di una
base dati e in più l’ereditarietà. Si ha la possibilità di esprimere query molto complesse
sugli oggetti, strumento molto potente quando usato in modo corretto.
Connection pooling
Dato che – come già detto – l’accesso a una base dati è un’operazione onerosa, si possono migliorare le prestazioni utilizzando uno strato intermedio chiamato connection
pooling: l’idea di base è andare a prendere le connessioni tramite un DataSource, riducendo così il ritardo introdotto nell’accesso alla base dati.
###
9. il framework spring
Generalità
Spring è un framework molto articolato. E’ basato su due concetti fondamentali:
• Dependency injection
◦ Creo e configuro programmi in modo dichiarativo
• Aggiunta di comportamenti cross-cutting
Include inoltre tutta una serie di strumenti utili per basi dati, servizi REST, meccanismi di
autenticazione e dispositivi mobili.
Accoppiamento di dati
Spring è l’applicazione più nota del principio di Inversion Of Control, tecnica di programmazione della OOP in cui l’accoppiamento tra gli oggetti è realizzato a run-time attraverso un IoC container. E’ opposta ai pattern di istanziazione di componenti, in cui è il componente che deve determinare l’implementazione della risorsa desiderata.
Per esempio una classe Automobile che necessita di un oggetto di tipo Motore, dichiarerà una variabile di istanza di tipo Motore e un metodo per impostare a run-time il riferimento all'oggetto.
Dependency injection
La dependency injection è una delle tecniche con le quali si può attuare l'IoC. Il programma si appoggia su uno strato software e consulta un file di configurazione: sulla
base di ciò che trova scritto, implementa l’oggetto e me lo restituisce.
E’ possibile fare injection tramite il costruttore. Si possono iniettare:
• proprietà
• inner bean
• collezioni di dati
Affinchè ci sia accoppiamento debole tra le classi, è necessario che se una classe ha
bsogno di usarne un’altra lo fa in quanto implementazione di un’interfaccia.
Autowire
Spring permette di non inizializzare esplicitamente le proprietà e i valori, ma di delegare
al framework tale compito. Questo aspetto va sotto il cappello di autowire: il framework
provvede automaticamente a riempire gli oggetti, a condizione che non ci siano ambiguità.
Vi sono diverse tipologie di autowire possibili:
• per nome
◦ si cerca, per ogni proprietà, il bean il cui nome coincide con il nome della proprietà
• per tipo
◦ si cerca, per ogni proprietà, il bean il cui tipo coincide con il tipo della proprietà
• per costruttore
◦ utile quando il costruttore di un bean necessita di parametri
• autodetect
◦ si prova prima per costruttore, poi per tipo
Contenitore
Da non confondere con il WebContainer (= Tomcat). E’ un componente software che si
occupa di creare gli oggetti, gestendone le dipendenze e il ciclo di vita.
Il contenitore più semplice è l’XmlBeanFactory, che crea dei bean e aggiunge le relative
dipendenze. Una sua estensione per le applicazioni web è l’ApplicationContext, che oltre
alla gestione del ciclo di vita dei bean aggiunge diverse cose tra cui il caricamento dinamico di risorse.
Bean in Spring
Un bean è un qualunque oggetto costruito a partire dal nome della sua classe e da
eventuali parametri inseriti nell’XML (dipendenze, ciclo di vita delle istanze, scope...)
Ciclo di vita
1. Il contenitore istanzia un bean specifico quando gli viene richiesto
2. Il bean viene popolato con le proprietà dichiarate nelle definizioni
3. Se implementa determinate interfacce vengono chiamati i metodi appropriati
4. Il framework invoca i metodi di preinizializzazione
5. Se specificato, viene invocato uno specifico metodo di inizializzazione
6. Se specificato, viene invocato un metodo di post-inizializzazione
Configurazione
La configurazione di Spring viene inserita in uno o più file XML che contiene le dichiarazioni dei bean. Durante l’esecuzione, il framework utilizza le definizioni per creare
un’istanza dei bean.
Costruire applicazioni web con Spring
1. La richiesta arriva al punto di accesso di Spring, ovvero il DispatcherServlet
2. Il DispatcherServlet, dopo aver capito qual è quello adeguato, consegna la richiesta a un controllore
3. Se il controllore lo prevede, alcune informazioni che compongono il modello vengono restituite all’utente e visualizzate nel browser
4. Il DispatcherServlet identifica la vista opportuna per mostrare i dati
5. Il DispatcherServlet consegna alla vista i dati presenti nel modello
DispatcherServlet
Servlet che gestisce tutte le richieste, comprese quelle per i contenuti statici.
Controllore
E’ possibile costruire un controllore per ogni attività gestita dall’applicazione. Le classi
controllore sono opportunamente mappate con annotazioni che specificano, tra le altre
cose, a quali indirizzi – e su quali metodi – devono rispondere.
Identificare e gestire le viste
A seconda del ViewResolver configurato, si hanno diverse regole. Noi usiamo l’InternalResolverViewResolver che risolve il nome logico di una vista in un oggetto di tipo View.
Per facilitare l’integrazione con le viste, Spring mette a disposizione alcune librerie di
tag.
Gestione di form
Spring offre tutta una serie di strumenti per gestire l’input. L’annotazione @RequestParam, ad esempio, è usata per iniettare i parametri della richiesta nel controllore. E’ an-
che possibile costruire classi che modellano il form e usare l’annotazione @ModelAttribute. Annotando opportunamente gli attributi degli oggetti POJO, infine, è possibile
esprimere vincoli sui valori.
###
10. rest - representational state transfer
Generalità
La caratteristica stateless di HTTP è stata vissuta come un grosso impiccio.
Per questo si è arrivati al concetto di sessione, che però è scomodo in realtà di comunicazione machine-to-machine (= GTT contatta Google Map per sapere qual è il percorso
migliore da una fermata all’altra). Non si vogliono avere dipendenze dal tipo di piattaforma ed è preferibile non essere legati a un singolo formato di risposta. Dev’essere semplice lo sviluppo e la manutenzione.
Una prima soluzione è stata ricorrere a SOAP. La cosa sembrava ragionevole perché supportava bene l’eterogeneità, ma ha come ✘ punto debole la scalabilità: per inviare, ad
esempio, una risposta di tipo booleano devo fare una richiesta di 5-10 KB e ricevere una
risposta di 5-10 KB. Inoltre, tutte le richieste SOAP vengono passate in POST perché non
si sa a priori se il dato è cacheabile, perdendo così le peculiarità di HTTP.
Il modello REST
Si è dunque cominciato a pensare allo stile REST: dato che esiste già un’infrastruttura di
trasporto sottostante, si cerca di sfruttarla dove possibile senza violare i suoi principi
base.
L’idea è quella di trasferire una rappresentazione dello stato all’utente, in modo che
l’utente possa riprenderla in qualunque momento. E’ dunque ottimo per realizzare applicazioni distribuite in cui le sessioni di lavoro sono tendenzialmente d’impiccio. Inoltre, la
caratteristica di HTTP che permette di richiedere diversi formati con Accept-Encoding è
ottima per negoziare il formato della risposta.
HTTP, inoltre, è ottimo in quanto i suoi metodi standard mappano perfettamente le operazioni CRUD.
Operazione
Metodo Oggetto
Creare una voce
POST
URL della collezione
Leggere un elenco di voci
GET
URL della collezione
Leggere una singola voce
GET
URL dell’elemento
Aggiornare una singola voce PUT
URL dell’elemento
Cancellare una singola voce DELETE
URL dell’elemento
Un server può ospitare uno o più servizi REST, ciascuno dei quali gestisce un insieme di
informazioni (= entità rappresentabili in modo digitale) con un ID univoco.
Implementare servizi REST
Si può banalmente scrivere un servlet, oppure ricorrere alla specifica JAX-RS che supporta completamente l’architettura REST. Il principio è che se si vuole fare un servizio REST
in Java si costruiscono opportunamente alcune classi annotate e si include Jersey.
Il framework Jersey
Jersey, infatti, mette a disposizione un servlet specializzato per servire le richieste rivolte a POJO debitamente annotati. E’ possibile, tramite le annotazioni:
• specificare l’URL sul quale è esposta la risorsa
• specializzarlo per un certo tipo di richiesta CRUD
•
•
indicare i parametri
negoziare il contenuto
Interagire con un server REST
Oltre a esporre servizi verso il resto del mondo possiamo anche consumarne. In aggiunta al servlet e al core, infatti, Jersey fornisce un client HTTP REST.
###
11. applicazioni web in tempo reale
Problema e possibili approcci
Ci sono situazioni in cui i servizi che si vogliono creare evolvono nel tempo indipendentemente dall’azione dell’utente. Interessante perché il client, senza installare codice dal
suo lato, può avere servizi a valore aggiunto. Realizzare tutto ciò con il classico modello
request/response, però, è un casino ed è poco scalabile.
Polling semplice
L’approccio più banale: chiedo continuamente se c’è qualcosa per me. ✘ Tiro giù client,
server e rete. Accantonato.
Long polling
E’ una sorta di polling con ritardo. L’approccio migliore è il seguente:
1. il browser fa una richiesta
2. il client tiene la richiesta appesa fin quando il server non comunica niente
3. quando il server deve comunicare qualcosa usa la richiesta per scrivere i dati, invia la risposta e chiude la connessione
4. il browser elabora i dati e fa immediatamente partire una nuova richiesta
Si faccia attenzione al fatto che il client potrebbe non contattare più il server. Devo dunque progettare bene per quanto tempo tenermi una richiesta in RAM. Inoltre, dato che le
varie richieste sono gestite con un thread pool, ✘ le risorse disponibili finiscono in fretta se ho un qualcosa che opera su grossa scala.
Uno dei grossi svantaggi del long polling è che ✘ manda una grossa quantità di header,
oltre ad avere tutta una serie di problemi aggiuntivi.
Streaming di contenuti
Può essere dunque una buona idea usare la connessione del client, tenendola sempre
aperta e inviando i dati in modalità chunked. Inviando chunk di dimensione 0 a intervalli
regolari posso accorgermi se il client è caduto.
✔ Non si ha timeout immediato, ma ci si avvicina parecchio. Lo svantaggio è dovuto al
supporto limitato da parte dei browser (specie quelli mobile) e dal fatto che ✘ le connessioni di invio e ricezione sono slegate tra loro. Inoltre anche questo modello soffre il
problema dei thread descritto in precedenza.
Servlet asincroni
Introdotti a partire da JavaEE 6. Si può annotare un server dicendo che è in grado di lavorare in modalità asincrona.
Le richieste vengono incapsulate in oggetti di tipo AsyncContext, che verranno poi utilizzate in seguito per generare la risposta. La gestione delle richieste asincrone presuppone che il server disponga di un meccanismo per organizzare e distribuire i messaggi: occorre fare attenzione ad evitare lock globali che serializzano l’accesso da parte di tutti i
client.
✘ Livello di efficienza molto limitato.
✘ La presenza dei proxy con i loro meccanismi di timeout complica tutto.
WebSocket
W3C ha standardizzato i WebSocket, ovvero l’equivalente di un socket TCP in un browser.
E’, di fatto, una connessione TCP mandata da un client verso un server che inizia con
una richiesta HTTP like. Se c’è supporto lato server si risponde con un 101 Protocol Switch: fatta questa negoziazione iniziale, esiste un canale TCP sul quale vengono inviati
messaggi di testo o binari, ping dal server al client, pong dal client al server. Questa
connessione resta aperta finché uno tra client e server non se ne va.
Non ho bisogno di gestire sessioni e il canale e full duplex.
Lato client
Un WebSocket si può creare solo da JavaScript.
1. Si verifica che il browser supporti i WebSocket
2. Si crea un WebSocket indicando la URL alla quale connettersi
3. Si registrano le callback, ovvero le funzioni da chiamare in caso di
◦ connessione a buon fine
◦ errore
◦ connessione chiusa
◦ messaggio dal server
Lato server
Bisogna creare classi opportunamente annotate in modo da specificare l’endpoint e cosa
fare in caso di @OnOpen, @OnClose, @OnMessage, @OnError. La classe Session modella il canale di comunicazione e offre due metodi (uno bloccante e uno basato su oggetti
Future) per interagire con il client remoto.
###
12. introduzione alle web application
Web applications
Un’applicazione per il web è paragonabile a una classica applicazione client-server, nella
quale però il client è più ricco e il server è più snello. Il software è sul server ed è scari cato dal client ogni volta che si connette.
✔
✔
✔
✔
✘
✘
✘
Aggiornamento e deployment facile
Compatibilità multipiattaforma
Ambiente più ricco dove interagire
Ready for mobile
Ambiente di sviluppo non troppo maturo
I browser non si comportano tutti allo stesso modo
Applicazione dipendente dalla rete e dal server
Fattori chiave
Per replicare le funzionalità delle applicazioni desktop devo avere:
• Interattività
◦ posso interagire perché c’è del Javascript che mi cambia la pagina
• Rich UI
◦ HTML5 ha introdotto nuove funzionalità per veicolare media diversi
• Responsiveness
◦ Con AJAX posso cambiare il contenuto della pagina senza doverla ricaricare
• Client-centric
◦ Molta della logica è sul client
• Efficienti per la rete
◦ Il trasferimento delle informazioni è relativo ai soli dati utilizzati
Single page application ❖
Una applicazione a pagina singola è una applicazione web o un sito web che si inserisce
in una singola pagina web con l'obiettivo di fornire un’esperienza utente più fluida simile
a una applicazione desktop.
Il lato client diventa più complesso e gestisce parti del business model, comunicando direttamente con lui anziché col server. Vengono scambiati solo i dati tramite AJAX.
Il lato server, invece, si occupa di gestire ad esempio l’autenticazione e la persistenza
dei dati sul database.
A differenza di un’applicazione PHP in cui le viste vengono ricreate sul server e scaricate
ogni volta dal client, la vista rimane fissa sul client.
AJAX ❖
AJAX sta per Asynchronous JavaScript and XML.
Permette lo sviluppo di applicazioni web interattive in cui i dati vengono scambiati in
background tra browser e server. In questo modo la pagina web è aggiornabile dinamicamente senza esplicito refresh dell’utente.
Per evidenziare le caratteristiche di asincronicità è bene ricorrere ad un esempio. In una
webapp tradizionale, ogni volta che l’utente invia i dati di un form, la pagina è inviata al
server. L’utente non vi può più interagire e il server restituirà una nuova pagina all’utente.
In una webapp asincrona, invece, l’utente invia solamente i dati al server. Grazie alle
callback registrate, il client è in grado di domandare al server solo i nuovi dati. L’utente
può continuare a interagire con la pagina in quanto la richiesta è fatta in background.
Inoltre, grazie alla logica trasferita sul client, la nuova pagina verrà costruita dal client
stesso.
###
13. javascript avanzato
Uno sguardo d’insieme
Javascript è un linguaggio particolare per diversi aspetti:
• Non è basato sulle classi, ma è ad oggetti
◦ Gli oggetti non hanno un modello rigido come quello delle classi Java
• Gli oggetti possono fare riferimento a un modello, ma il modello è a sua volta un
altro oggetto
• Le funzioni sono oggetti
◦ Possono essere copiate, assegnate, sovrascritte
◦ Posso aggiungere proprietà
• Lo scope è a livello di funzione
• E’ interpretato al volo e non compilato
◦ Per migliorare le prestazioni ci sono dei motori che lo compilano
• Le variabili sono dinamicamente tipizzate
• L’esecuzione è single-thread
◦ Se io blocco il codice (es.: alert) tutto il resto rimane fermo
Attenzione a...
Variabili locali e globali ❖
Se una variabile è dichiarata tramite var all’interno di una funzione, tale variabile è locale alla funzione.
function esempio() {
var a = 0;
b = 0;
}
var c = 0;
Solo a è una variabile locale. Questa caratteristica è sfruttata nella definizione dei moduli.
Esecuzione a thread singolo
Si faccia attenzione al fatto che in Javascript l’esecuzione è single-thread: al contrario
ad esempio di Java, il codice in Javascript non può essere interrotto in ogni momento.
Flusso di caricamento
Javascript è caricato ed eseguito in corrispondenza del blocco head, motivo per cui il seguente esempio
<html>
<head>
<script>document.getElementbyId(“box").innerHTML = “Hello";</script>
</head>
<body>
<div id="box">Ciao</div>
</body>
</html>
restituisce Ciao. E’ dunque buona norma caricare il Javascript dopo il body.
Oggetti in Javascript
Gli oggetti sono dinamici: le proprietà possono essere aggiunte, rimosse e cambiate.
var cat = {
color: “gray",
age: 3
};
Possono essere creati con le sintassi
• var o = new Object();
• var o = {};
Funzioni in Javascript
In Javascript le funzioni sono oggetti e possono essere assegnate a variabili e passate a
funzioni. Si possono, inoltre, settare proprietà e invocare metodi su di esse.
Pattern classico (definizione + invocazione)
function esempio() {
return 7;
}
var a = esempio();
Funzioni anonime con successiva invocazione (ed assegnazione di parametro) ❖
var esempio = function() {return 1;}
var e = esempio;
var x = e();
e.parametro = 2;
/* x = 1 */
Argomenti
Sempre passati per valore. Sono rappresentati in un array arguments, del quale possiamo sapere la dimensione.
function esempio() {
if (arguments.length === 2)
return arguments[0] + arguments[1];
}
var x = esempio(3, 5);
/* x = 8 */
Hoisting ❖
Dichiarazioni di funzioni e variabili sono sempre “spostate" in alto nel loro scope.
function esempio() {
var a = b;
var b = 5;
...
}
Nell’esempio a non vale 5, ma il codice è corretto.
altroesempio();
function altroesempio() {alert(“Ciao!");}
Questo esempio stampa correttamente “Ciao!".
Array in Javascript
Gli array sono sequenze numerate di elementi. Hanno una proprietà length che dice
qual è l’indice dell’ultimo elemento. Sugli array è possibile iterare. Sono inclusi metodi
built-in per ordinare gli array, invertirli, …
this e il suo scope ❖
Fa riferimento all'oggetto corrente. La parola chiave this si riferisce solitamente
all'oggetto window se utilizzata al di fuori dell'ambito di un altro oggetto.
In alcuni casi è necessario utilizzare una variabile d’appoggio self: una nested function
vede le variabili della funzione in cui è definita, ma this può non rispettare lo stesso
scope (in quanto non eredita il this del chiamante)
Nested functions e closures ❖
Una nested function è una funzione definita dentro un’altra funzione. Può riferirsi alle
variabili definite nella funzione padre. Non vale il viceversa
function esempio(a, b) {
function sottoesempio() { return a + b + 5; }
return sottoesempio(a, b);
}
Si ha una closure quando la funzione nested viene eseguita in un altro contesto.
function esempio() {
var a = 5;
function sottoesempio() {return a;}
return sottoesempio();
}
e = esempio();
alert(e());
###
14. angular js
Il paradigma MVC
MVC offre una chiara separazione dei compiti.
• Modello
◦ Parla al server usando un’architettura RESTful
• Vista
◦ Generata da un template
• Controllore
◦ Colla tra modello e vista
◦ Gestisce le interazioni utente
Architettura del framework Angular JS ❖
Angular JS è basato sul pattern MVC, rivisto in una forma MVVM, sigla che sta per Model
– View – View-Model. Le principali differenze sono:
• Si passa dalle viste passive che delegano tutto al controllore a viste attive
• Il controllore è “sostituito" dal view-model, che mantiene lo stato della vista e manipola il modello a seconda delle azioni sulla vista
Interessante notare che la vista non deve conoscere niente al riguardo del controllore e
il controllore non deve conoscere niente al riguardo della vista. L’importante è che tra i
due ci sia una colla, ovvero $scope: il controllore popola $scope per disegnare la vista e
si appoggia su un provider per recuperare i dati da qualche risorsa.
Integra i concetti di:
• Dependency injection
◦ Creazione di componenti, risoluzione delle dipendenze
• Two way data binding
◦ Ogni cambiamento della vista è immediatamente riflesso nel modello
◦ Ogni cambiamento del modello è immediatamente propagato alla vista
Two way data binding ❖
Un esempio di two way data binding può essere il seguente
<html data-ng-app="myApp">
<input type="text" data-ng-model="var" />
{{var}}
</html>
{{var}} mostrerà l’input della casella di testo in tempo reale.
Routing in Angular JS ❖
In Angular JS, il componente di routing permette di caricare una nuova vista nella nostra
SPA a seconda dell’azione compiuta. Più nel dettaglio, si specifica in $routeProvider:
• un URL, che rappresenta anche lo stato dell’applicazione
• un template della vista associata all’URL
• il controllore atto a gestire le funzioni da svolgere e la successiva vista da selezionare
REST e Angular JS ❖
REST (Representational State Transfer) si basa su un protocollo di comunicazione state-
less, client-server, cacheable. Sfrutta HTTP come protocollo “di appoggio". L’implementazione lato server può avvenire con Jersey, che mette a disposizione un servlet specializzato per servire le richieste rivolte a POJO debitamente annotati.
Per sfruttare questi servizi programmaticamente lato client con Angular JS si può usare
$resource, che permette di interagire con servizi RESTful. In particolare, nella factory si
andranno a specificare i dettagli del servizio (endpoint, metodo, content-type...) che potrà poi essere consumato, al bisogno, dal controllore.
###
15. responsive web design e css3
Le ragioni ❖
Si è cominciato a parlare di design responsivo in quanto per accedere al web si usano
una moltitudine di dispositivi con caratteristiche differenti, sia per quanto riguarda le dimensioni che per quanto riguarda le varie funzionalità (sui PC, ad esempio, non ho il
touch).
Un possibile approccio sarebbe quello di distinguere in base allo user-agent e restituire
pagine personalizzate per ogni dispositivo, ma l’operazione non dovrebbe essere di
stretta competenza del server. Un approccio migliore prevede l’utilizzo del responsive
web design, in grado di fornire un’esperienza ottimale su più dispositivi con dei piccoli
aggiustamenti a livello di dimensioni e posizione degli elementi.
I tre pilastri fondamentali su cui poggia il design responsivo sono:
• CSS 3 media queries
◦ Applicazione selettiva del CSS in base, ad esempio, alle dimensioni della finestra
• Layout della pagina fluido
◦ Le dimensioni degli elementi sono espresse in percentuale per adattarsi a una
moltitudine di schermi
• Immagini flessibili
◦ Si ridimensionano automaticamente alla larghezza massima del contenitore
Twitter Bootstrap e media queries ❖
In Twitter Bootstrap le media queries sono massicciamente utilizzate. Un classico esempio è dato dal ridimensionamento di una pagina HTML simile alla seguente:
<html>
<body>
<div class="row">
<div class="col-md-6">Prima colonna</div>
<div class="col-md-6">Seconda colonna</div>
</div>
</body>
</html>
Sopra il breakpoint definito da md, le colonne appariranno una di fianco all’altra. Sotto
tale dimensione della finestra, le colonne appariranno una sotto l’altra.
Mobile-first design ❖
Sotto il cappello di mobile first design va quella filosofia che prevede di progettare la visualizzazione di un sito Internet considerando di avere a che fare con schermi piccoli. In
seguito, poi, si passa agli schermi grandi. Motivazioni e vantaggi sono riassumibili in:
• Uso sempre maggiore dei dispositivi mobili
• Concentrazione sui dettagli fondamentali
• Utilizzo delle caratteristiche avanzate dei dispositivi mobili
CSS3 vs. resto del mondo ❖
Prima di CSS3 molte cose si realizzavano con Javascript e con, ad esempio, il download
di immagini. Questi due aspetti si scontrano molto con la separazione dei compiti (in Javascript non dovrei occuparmi di grafica, le immagini non dovrebbero essere usate per
scopi decorativi).
Usare CSS3, inoltre, renderizza la pagina più velocemente rispetto alle stesse operazioni
fatte con Javascript.
Due esempi in cui l’uso di CSS3 è più indicato rispetto ad altri strumenti:
• Sfondi trasparenti
◦ Nell’era pre-CSS3 bisognava fare dei background con immagini gif 1x1 ripetute, oggi si può usare l’attributo opacity
• Transizioni e rotazioni
◦ Nell’era pre-CSS3 bisognava utilizzare lente funzioni in Javascript, oggi si può
usare l’attributo transition
###
16. jquery
Sguardo d’insieme
jQuery è una libreria di funzioni Javascript, per le applicazioni web, che si propone come
obiettivo quello di semplificare la programmazione lato client delle pagine HTML.
L’oggetto $ e i suoi comportamenti ❖
L'oggetto principale, di nome jQuery, è genericamente utilizzato tramite il suo alias, il
carattere $, per mantenere uniformità con la libreria Prototype.
Ha diversi comportamenti in funzione dell’argomento ricevuto:
• Creazione: $(‘<div>’) crea un elemento nel DOM
• Selezione: $(‘div’) seleziona tutti i div nel DOM
• Promozione: $(‘event.target’) wrappa elementi nel DOM in un oggetto jQuery
• Esecuzione di funzioni: $(function() {...}); esegue function quando il DOM è
caricato
Gestione degli eventi in jQuery ❖
jQuery fornisce un API che funziona in tutti i browser per gestire gli eventi. Supponiamo
di avere il seguente codice HTML
<a href="link.html"><b>Cliccami</b></a>
e il seguente script di gestione
$("a").click(function() {
alert(“Ciao!”);
alert(event.target.nodeName);
$(this).attr("href", "http://www.google.com/");
});
Cliccando su Cliccami – che è surroundato in b – mi viene mostrato l’alert, anche se questo è registrato sul click dell’elemento a. Questo perché jQuery risale l’albero del DOM,
trova l’evento che abbiamo associato al tag a e lo lancia. Questo è un modo intelligente
per gestire gli elementi annidati slegandosi dal focus reale del nostro mouse.
Si faccia attenzione alla differenza tra event.target e this. Il primo si riferisce all’elemento effettivamente cliccato, mentre il secondo si riferisce all’elemento su cui è registrato
il listener.
Live events ❖
Sono frequenti le situazioni in cui vengono creati elementi “al volo”. Ad esempio,
supponiamo di avere
<div id=”box”>
<a href=”www.google.it” \>
<a href=”www.repubblica.it” \>
<a href=”www.ansa.it” \>
</div>
e di aver registrato su tutti i link un handler con la bind().
function handler(event) {alert(“Hai cliccato un link”);}
$(“a”).bind(“click”, {}, handler);
Se creassi un nuovo elemento a, questo non avrebbe lo stesso handler. L’idea è dunque
quella di ricorrere a delegate(): questo ci permette di registrare un solo listener a un
elemento parent che verrà scatenato per tutti i discendenti che matchano un dato
selettore, sia quelli esistenti che quelli futuri.
Chaining ❖
Come suggerito dal nome, il chaining è quella caratteristica che permette la concatenazione del codice. Affinchè sia possibile, è necessario che un metodo jQuery ritorni un altro oggetto jQuery (e la cosa è fatta dalla maggior parte dei metodi).
Un esempio può essere il seguente:
$(‘div.email’).click().addClass(‘read’);
In questo modo, cliccando su un <div class="email"> mi verrà aggiunta la classe read.
AJAX con jQuery ❖
Per usare AJAX con jQuery si può ricorrere al metodo load() applicato su un oggetto Javascript. Questo metodo riceve come parametri:
• l’URL a cui effettuare la richiesta asincrona
• un dato (oggetto o stringa) da postare con GET o POST al server insieme alla
richiesta
• una callback jQuery che verrà eseguita quando la richiesta AJAX sarà completata,
con successo o meno
La callback a sua volta riceve tre parametri:
• risposta
• stato
◦ success, error, timeout...
• oggetto XMLHttpRequest
◦ usato per effettuare la richiesta
Il contenuto andrà a finire nell’elemento del DOM associato all’oggetto Javascript su cui
è stata chiamata la load().
Uso delle promise ❖
A partire da jQuery 1.5, l’oggetto jqXHR che viene ritornato da una chiamata ad $.ajax
implementa l’interfaccia Promise.
var jqxhr = $.ajax("example.php");
jqxhr.done(function() { alert("success"); })
jqxhr.fail(function() { alert("error"); })
jqxhr.always(function() { alert("complete"); });
/* Cose mie. */
jqxhr.always(function() { alert("another complete"); });
Possiamo dunque specificare cosa fare quando la chiamata AJAX termina con successo,
con fallimento e in ogni caso. Usare le promise è fonte di numerosi vantaggi:
• possono essere chiamate più volte per associare più di un handler
• rendono il codice più leggibile
• una volta che sono stati registrati, gli handler vengono invocati immediatamente
se l’operazione AJAX è completata
###
17. html5
Server sent events ❖
Rispetto ai WebSockets – l’equivalente dei socket TCP nei browser, che creano una connessione two-way server-client – e al long polling, in cui è il server gestisce i messaggi, i
Server Sent Events modellano quelle situazioni in cui il client vuole effettuare una sottoscrizione a notifiche/messaggi generati dal server e non ha quindi bisogno di un canale
full-duplex.
L’API è una libreria HTLM5 che permette di delegare al browser il monitoraggio di una
sorgente di dati e di ottenere notifiche “push” dal server ad ogni nuovo evento. E’ basata su un oggetto EventSource da creare e da far puntare alla pagina da monitorare. Su
questo oggetto si registrano poi uno o più listener per l’evento message.
WebSockets ❖
WebSocket è un protocollo che fornisce canali di comunicazione full-duplex su di una
singola connessione TCP. E’ gestito dal browser e condivide la porta 80 di HTTP.
Dopo una fase iniziale in cui “nasce” come connessione HTTP, switcha come WebSocket
sfruttando lo scambio dell’header Upgrade. La connessione HTTP a questo punto è rimpiazzata dalla connessione WebSocket, sulla stessa connessione TCP/IP. Essendo fullduplex si adatta bene, ad esempio, a implementare una chat.
L’API è basata sull’oggetto WebSocket. Permette di registrare funzioni sugli eventi onopen, onerror, onmessage, onclose.
###
18. object oriented programming in javascript
Costruttore ❖
Per evitare di dover ridefinire da zero oggetti che hanno la stessa struttura possiamo ricorrere ad un costruttore. Un costruttore non è altro che una normale funzione Javascript invocata mediante l’operatore new.
function Oggetto(proprieta) {
this.proprieta = proprieta;
}
var esempio = new Oggetto(“qualcosa”);
L’operatore new ❖
Infatti, quando una funzione è chiamata con l’operatore new, la sua variabile this punterà a un nuovo oggetto che verrà automaticamente ritornato.
Prototipo ❖
Ogni funzione è creata con una proprietà prototype, a sua volta un oggetto che ha una
proprietà chiamata constructor, che punta alla funzione su cui è proprietà. La proprietà
prototype è condivisa tra tutte le istanze create con il pattern new + constructor.
Influenza del prototipo sul lookup delle proprietà ❖
In lettura si guardano prima le proprietà dell’oggetto in sè e poi, ricorsivamente, si guardano quelle dei vari prototipi. In scrittura non si va mai nel prototipo: questo vuol dire
che si può effettuare override di proprietà.
Esempio di definizione di una classe in Javascript ❖
function NumeroComplesso(r, i) {
this.r = r;
this.i = i;
};
NumeroComplesso.prototype.modulo = function() {
return Math.sqrt(this.r*this.r + this.i*this.i);
}
Esempio di scope privato ❖
function NumeroComplesso(r, i) {
this.r = r;
this.i = i;
function _reset() {this.r = 0; this.i = 0;}
};
La funzione _reset è dichiarata all’interno del costruttore e può essere chiamata solo da
metodi privilegiati, nonché dal costruttore dell’oggetto.
Modularizzazione ❖
Dividere il codice in moduli evita di inquinare lo scope globale e soprattutto permette di
assemblare codice dalle sorgenti più disparate senza interferenze. Per far questo si possono sfruttare, in assenza di costrutti specifici nel linguaggio Javascript:
• il fatto che la visibilità in Javascript è a livello di funzione
• le funzioni immediatamente invocate
Ci sono due modi di costruire un modulo in Javascript. Il primo è esporre un’interfaccia
pubblica seguendo il revealing pattern.
it.polito.esempio = (
function namespace () {
var public_var;
var private_var;
return {
var: public val
}
}
) ();
Un sistema più usato è quello della creazione di oggetti utilizzando i costruttori.
var Esempio = (
function namespace () {
Esempio = function(id) {
this._id = id;
};
Esempio.prototype = {
getId: function() { return this._id; },
version: “1.0”,
constructor: Esempio;
};
}
) ();
return Esempio;
Ereditarietà ❖
Javascript implementa l’eredità permettendo di associare a un oggetto del prototipo una
funzione costruttore. Più nello specifico, se la classe Figlia estende la classe Padre, allora
Figlia.prototype deve ereditare Padre.prototype. Così facendo, le istanze di Figlia
erediteranno da Figlia.prototype – e, per estensione, da Padre.prototype.
###