SeamFramework.orgCommunity Documentation
Seam fornisce un metodo per accedere in modo remoto i componenti da una pagina web, usando AJAX (Asynchronous Javascript and XML). Il framework per questa funzionalità viene fornito con quasi nessuno sforzo di sviluppo - i componenti richiedono solamente una semplice annotazione per diventare accessibile via AJAX. Questo capitolo descrive i passi richiesti per costruire una pagina web abilitata a AJAX, poi spiega con maggior dettaglio le caratteristiche del framework Seam Remoting.
Per usare remoting, il resource servlet di Seam deve essere innanzitutto configurato nel file web.xml
:
<servlet>
<servlet-name
>Seam Resource Servlet</servlet-name>
<servlet-class
>org.jboss.seam.servlet.SeamResourceServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name
>Seam Resource Servlet</servlet-name>
<url-pattern
>/seam/resource/*</url-pattern>
</servlet-mapping
>
Il passo successivi è importare il Javascript necessario nella propria pagina web. Ci sono un minimo di due script da importare. Il primo contiene tutto il codice del framework lato client che abilita le funzionalità di remoting:
<script type="text/javascript" src="seam/resource/remoting/resource/remote.js"
></script
>
Il secondo script contiene gli stub e le definizioni tipo per i componenti da chiamare. Viene generato dinamicamente basandosi sull'interfaccia locale dei propri componenti, ed include le definizioni tipo per tutte le classi che possono essere usate per chiamare i metodi remoti dell'interfaccia. Il nome dello script riflette il nome del componente. Per esempio se si ha un bean di sessione stateless annotato con @Name("customerAction")
, allora il tag dello script dovrebbe essere simile a:
<script type="text/javascript"
src="seam/resource/remoting/interface.js?customerAction"
></script
>
Se si vuole accedere a più di un componente dalla stessa pagina, allora li si includa tutti come parametri nel tag script:
<script type="text/javascript"
src="seam/resource/remoting/interface.js?customerAction&accountAction"
></script
>
In alternativa si può usare il tag s:remote
per importare il Javascript richiesto. Si separi ciascun componente o nome di classe che si vuole importare con una virgola:
<s:remote include="customerAction,accountAction"/>
L'interazione lato client con i componenti viene eseguita tutta tramite l'oggetto Javascript Seam
. Quest'oggetti è definito in remote.js
, e lo si userà per fare chiamate asincrone verso il componente. E' suddiviso in due aree di funzionalità; Seam.Component
contiene metodi per lavorare con i componenti e Seam.Remoting
contiene metodi per eseguire le richieste remote. La via più facile per diventare familiare con quest'oggetto è cominciare con un semplice esempio.
Si cominci con un semplice esempio per vedere come funziona l'oggetto Seam
@Stateless
@Name("helloAction")
public class HelloAction implements HelloLocal {
public String sayHello(String name) {
return "Hello, " + name;
}
}
E' anche necessario creare un'interfaccia locale per il nuovo componente - tenete a mente in particolare l'annotazione @WebRemote
, poiché è necessaria a rendere un metodo accessibile via remoting:
@Local
public interface HelloLocal {
@WebRemote
public String sayHello(String name);
}
Quello è tutto il codice da scrivere.
Se nel metodo annotato con @WebRemote
viene eseguita un'operazione di persistenza, occorre marcare il metodo anche con l'annotazione @Transactional
. Altrimenti, senza questa indicazione extra, il metodo non viene eseguito all'interno di una transazione. Ciò perché, a differenza di una richiesta JSF, Seam non include automaticamente una richiesta remota in una transazione.
Ora, per quantro riguarda la pagina web - bisogna creare una nuova pagina e importare il componente helloAction
:
<s:remote include="helloAction"/>
Per rendere l'esperienza dell'utente veramente interattiva, si aggiunga un bottone alla pagina:
<button onclick="javascript:sayHello()"
>Say Hello</button
>
Bisognerà anche aggiungere uno script per far fare qualcosa al bottone quando viene cliccato:
<script type="text/javascript">
//<![CDATA[
function sayHello() {
var name = prompt("What is your name?");
Seam.Component.getInstance("helloAction").sayHello(name, sayHelloCallback);
}
function sayHelloCallback(result) {
alert(result);
}
// ]]>
</script
>
Abbiamo finito! Installate l'applicazione e andate col browser alla pagina creata. Premete il pulsante e inserite un nome quando richiesto. Una finestra mostrerà il messaggio di saluto che confermerà che la chiamata è avvenuta con successo. Per risparmiare tempo, cercate il codice dell'esempio Hello World nella directory /examples/remoting/helloworld
di Seam.
Quindi, cosa fa realmente il codice del nostro script? Dividiamolo in pezzi più piccoli. Tanto per iniziare, dal listato Javascript si vede che abbiamo implementato due metodi - il primo serve a chiedere all'utente il suo nome e a fare una richiesta remota. Guardate la seguente linea:
Seam.Component.getInstance("helloAction").sayHello(name, sayHelloCallback);
La prima parte di questa linea, Seam.Component.getInstance("helloAction")
restituisce un proxy, o "stub", del componente helloAction
. Possiamo chiamare i metodi di questo componente usando tale stub, che è ciò che accade nel resto della linea: sayHello(name, sayHelloCallback);
.
Nel suo complesso questa linea di codice invoca il metodo sayHello
del componente, passandogli name
come parametro. Il secondo parametro, sayHelloCallback
non è un parametro del metodo sayHello
del componente, ma, invece, comunica al Remoting framework di Seam che, una volta ricevuta la risposta alla richiesta, deve passarla al metodo Javascript sayHelloCallback
. Questo parametro di callback è completamente opzionale, quindi sentitevi liberi di non usarlo se chiamate un metodo che restituisce void
o se non siete interessati al risultato.
Il metodo sayHelloCallback
, una volta ricevuta la risposta, mostra un messaggio di avviso con il risultato della chiamata.
L'oggetto Javascript Seam.Component
fornisce un serie di metodi lato client per lavorare con i componenti Seam dell'applicazione. I due metodi principali, newInstance()
e getInstance()
sono documentati nelle sezioni successive, comunque, la loro differenza principale sta nel fatto che newInstance()
crea sempre una nuova istanza di un tipo di componente, mentre getInstance()
restituisce un'istanza singleton.
Usate questo metodo per creare una nuova istanza di un entity o di un componente Javabean. L'oggetto restituito da questo metodo avrà gli stessi metodi get/set della sua controparte lato server, o, di preferenza, sarà possibile accedere ai suoi campi direttamente. Ad esempio, prendete il seguente componente Seam:
@Name("customer")
@Entity
public class Customer implements Serializable
{
private Integer customerId;
private String firstName;
private String lastName;
@Column public Integer getCustomerId() {
return customerId;
}
public void setCustomerId(Integer customerId} {
this.customerId = customerId;
}
@Column public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@Column public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Per creare un Customer lato client, bisognerebbe scrivere il codice seguente:
var customer = Seam.Component.newInstance("customer");
Allora da qui è possibile valorizzare i campi dell'oggetto customer:
customer.setFirstName("John");
// Oppure si pu� impostare direttamente i campi
customer.lastName = "Smith";
Il metodo getInstance()
viene usato per ottenere un riferimento allo stub di un componente Seam di tipo session bean, che può essere utilizzato per eseguire chiamate remote al componente stesso. Questo metodo restituisce un singleton del componente specificato, così che chiamarlo due volte di fila passando lo stesso nome come parametro restituirà la stessa istanza del suddetto componente.
Per continuare l'esempio precedente, se è stato creato un nuovo customer
e si vuole salvarlo, occorre passarlo al metodo saveCustomer()
del componente customerAction
:
Seam.Component.getInstance("customerAction").saveCustomer(customer);
Passare un oggetto a questo metodo, restituirà il suo nome di componente, se è un componente, o null
se non lo è.
if (Seam.Component.getComponentName(instance) == "customer")
alert("Customer");
else if (Seam.Component.getComponentName(instance) == "staff")
alert("Staff member");
La maggior parte delle funzionalità lato client di Seam Remoting sono contenute nell'oggetto Seam.Remoting
. Mentre non dovrebbe essere necessario chiamare direttamente la maggior parte di questi metodi, ce ne sono un paio che meritano di essere menzionati per la loro importanza.
Se l'applicazione contiene o usa classi Javabean che non sono componenti Seam, potrebbe essere necessario creare questi tipi sul lato del client per passarli come parametri al metodo del componente che si vuole chiamare. Si usi il metodo createType()
per creare un'istanza di tale tipo. Come parametro occore passare il nome completo della classe Java:
var widget = Seam.Remoting.createType("com.acme.widgets.MyWidget");
Seam Remoting supporta anche il calcolo di espressioni EL, che forniscono un altro metodo conveniente per ottenere dati dal server. Usando la funzione Seam.Remoting.eval()
, è possibile calcolare un'espressione EL da remoto sul server e restituire il valore risultante a un metodo callback lato del client. Questa funzione accetta due parametri, di cui il primo è l'espressione EL da calcolare e il secondo il metodo callback da invocare con il valore dell'espressione. Ecco un esempio:
function customersCallback(customers) {
for (var i = 0; i < customers.length; i++) {
alert("Got customer: " + customers[i].getName());
}
}
Seam.Remoting.eval("#{customers}", customersCallback);
In questo esempio l'espressione #{customers}
è calcolata da Seam, e il suo valore (in questo caso una lista di oggetti customer) è restituita al metodo customersCallback()
. E' importante ricordare che occorre importare i tipi degli oggetti restituiti in questo modo (via s:remote
) per poterli usare nel Javascript. Così per lavorare con una lista di oggetti customer
, è necessario importare il tipo customer
:
<s:remote include="customer"/>
Nella sezione di configurazione vista sopra, l'interfaccia, o "stub", del componente è importata nella pagina o attraverso seam/resource/remoting/interface.js
o usando la tag s:remote
.
<script type="text/javascript"
src="seam/resource/remoting/interface.js?customerAction"
></script
>
<s:remote include="customerAction"/>
Includenco questo script nella pagina, le definizioni delle interfacce del componente, più quelle di ogni di ogni altro componente o tipo necessario a eseguire i metodi del componente in questione sono generate e rese visibili perché il framework di remoting possa utilizzarle.
E' possibile generare due tipi di stub client, stubs "eseguibili" stubs e stubs "tipo". Gli stubs eseguibili sono dotati di comportamento, e sono usati per eseguire metodi dei componenti session bean, mentre gli stubs tipo servono a contenere dello stato e rappresentano i tipi che possono essere passati come parametri o restituiti come risultati.
Il tipo di stub client che viene generato dipende dal tipo di componente Seam. Se il componente è un session bean, allora sarà generato uno stub eseguibile, altrimenti, se si tratta di un entity bean o di un Java bean, sarò generato uno stub tipo. Vi è una sola eccezione a questa regola; se il componente è un Javabean (cioè non è né un session bean né un entity bean) e uno dei suoi metodi è annotato con @WebRemote, allora sarà generato uno stub eseguibile invece di uno stub tipo. Questo consente di usare il remoting per chiamare i componenti JavaBean in un ambiente non EJB dove non occorre avere accesso ai session bean.
L'oggetto contesto (Seam Remoting Context) contiene informazioni aggiuntive che sono inviate e ricevute come parte del ciclo di richiesta/risposta. Attualmente contiene l'id della conversazione, ma potrebbe essere espanso in futuro.
Se si vuole usare le chiamate remote all'interno di una conversazione, bisogna essere in grado di leggere o scrivere l'id della conversazione nell'oggetto contesto (Seam Remoting Context). Per leggere l'id della conversazione dopo avere fatto una richiesta remota occorre chiamare Seam.Remoting.getContext().getConversationId()
. Per scriverlo prima di fare una richiesta, bisogna chiamare Seam.Remoting.getContext().setConversationId()
.
Se l'ID della conversazione non è stato esplicitamente valorizzato usando Seam.Remoting.getContext().setConversationId()
, allora sarà assegnato in automatico il primo ID valido restituito da una chiamata remota. Se si sta lavorando con più conversazioni all'interno della pagina, allora può essere necessario indicare esplicitamente il valore dell'ID della conversazione prima di ciascuna chiamata. Se si sta lavorando con una sola conversazione, non occorre fare nulla di speciale.
In alcuni casi può essere necessario fare una chiamata remota all'interno della conversazione della pagina corrente. A questo scopo bisogna esplicitamente valorizzare l'ID della conversazione a quello della pagina prima di fare la chiamata remota. Questo piccolo estratto di Javascript assegna come ID della conversazione da usare per le chiamate remote quello della conversazione della pagina corrente:
Seam.Remoting.getContext().setConversationId( #{conversation.id} );
Seam Remoting permette di eseguire più chiamate a componenti all'interno di una singola richiesta. Si raccomanda di usare questa funzionalità ogni volta che si riveli appropriato allo scopo di ridurre il traffico di rete.
Il metodo Seam.Remoting.startBatch()
avvierà un nuovo batch, e tutte le chiamate a componenti eseguite in seguito saranno messe in coda invece che inviate immediatamente. Quando tutte le chiamate desiderate saranno state aggiunte al batch, il metodo Seam.Remoting.executeBatch()
invierà una singola richiesta con la coda delle chiamate al server, dove saranno eseguite nell'ordine specificato. Dopo l'esecuzione delle chiamate, una singola risposta con tutti i valori di ritorno sarà restituita al client e le funzioni callback (se specificate) saranno chiamate nello steso ordine di esecuzione.
Se si da il via ad un nuovo batch usando il metodo startBatch()
, ma successivamente si decide di non inviarlo, il metodo Seam.Remoting.cancelBatch()
annullerà tutte le chiamate già in coda e uscirà dalla modalità batch.
Per vedere un esempio di batch in azione, date un'occhiata a /examples/remoting/chatroom
.
Questa sezione descrive il supporto per i tipi di dati di base. Sul lato del server tali valori generalmente sono compatibili o con il proprio tipo primitivo o con il corrispondente tipo wrapper.
Quando si settano i valori di parametri stringa, basta usare oggetti Javascript di tipo String.
Sono supportati tutti i tipi numerici di Java. Sul client viene sempre serializzata la rappresentazione come stringhe dei valori numerici e sul server essi sono riconvertiti nei rispettivi tipi di destinazione. La conversione in un tipo primitivo o nel suo corrispondente wrapper è supportata per i tipi Byte
, Double
, Float
, Integer
, Long
e Short
.
In generale questi saranno o entity di Seam o componenti JavaBean o qualche altra classe che non corrisponde a un componente. Bisogna usare il metodo appropriato (o Seam.Component.newInstance()
per i componenti o Seam.Remoting.createType()
per tutto il resto) per creare una nuova istanza dell'oggetto.
E' importante notare che solo oggetti creati da uno di questi due metodi dovrebbero essere usati come valori parametrici, ogni volta che il parametro non corrisponde ad uno degli altri tipi validi menzionati altrove in questa sezione. In alcune situazioni può darsi che vi sia un metodo del componente per il quale non si può determinare l'esatto tipo del parametro, come in questo caso:
@Name("myAction")
public class MyAction implements MyActionLocal {
public void doSomethingWithObject(Object obj) {
// code
}
}
In questo caso si potrebbe voler passare come parametro un'istanza del componente myWidget
, ma l'interfaccia di myAction
non include myWidget
poiché esso non è referenziato direttamente da nessun altgro metodo. Pe risolvere la questione, MyWidget
deve essere importato esplicitamente:
<s:remote include="myAction,myWidget"/>
Ciò permetterà all'oggetto myWidget
di essere creato con Seam.Component.newInstance("myWidget")
, che può allora essere passato a myAction.doSomethingWithObject()
.
I valori delle date sono serializzati in una rappresentazione di Strina che è accurata al millisecondo. Lato client, si usi un oggetto Date Javascript per lavorare con i valori delle date. Late server, si usi java.util.Date
(o discendenti, come le classi java.sql.Date
o java.sql.Timestamp
).
Sul client, le enum sono trattate come stringhe. Quando si valorizza un parametro enum, basta usare la rappresentazione stringa della enum. Si prenda il componente seguente come esempio:
@Name("paintAction")
public class paintAction implements paintLocal {
public enum Color {red, green, blue, yellow, orange, purple};
public void paint(Color color) {
// code
}
}
Per chiamare il metodo paint()
con il colore red
, occorre passare come parametro il literal di tipo String:
Seam.Component.getInstance("paintAction").paint("red");
E' vero anche l'inverso - cioè, se il metodo di un componente restituisce un parametro di tipo enum (o contiene un campo enum in un qualunque punto del grafo di oggetti restituito) allora esso sul client essa sarà rappresentato come stringa.
Le bag coprono tutti i tipi collezione inclusi array, collezioni, liste, insiemi, (ma escluse le mappe - per le quali bisogna fare riferimento alla prossima sezione), e sul client sono implementate come array Javascript. Quando si chiama un metodo che accetta uno di questi tipi come parametro, il parametro dovrebbe essere un array di Javascript. Se un metodo restituisce uno di questi tipi, anche il valore restituito sarà un array di Javascript. Il framework sul server è sufficientemente intelligente da convertire la bag nel tipo adatto al metodo chiamato.
Poichè in Javascript le mappe non sono supportate nativamente, insieme al framework viene fornita una semplice implementazione di Map. Per creare un oggetto Map che possa essere usato come parametro di una chiamata remota, bisogna creare un nuovo oggetto di tipo Seam.Remoting.Map
:
var map = new Seam.Remoting.Map();
Questa implementazione Javascript fornisce i metodi di base per lavorare con le mappe: size()
, isEmpty()
, keySet()
, values()
, get(key)
, put(key, value)
, remove(key)
e contains(key)
. Ciascuno di essi è equivalente alla controparte Java. Quando il metodo restituisce una collezione, come con keySet()
e values()
, sarà restituito un Array Javascript che contiene le chiavi o i valori (ripettivamente).
Per aiutare l'individuazione dei bug, è possibile abilitare una modalità di debug che mostrerà in una finestra di popup il contenuto di tutti i pacchetti inviati avanti e indietro tra client e server. Per abilitare tale modalità, o si chiama il metodo setDebug()
in Javascript:
Seam.Remoting.setDebug(true);
O lo si configuri via components.xml:
<remoting:remoting debug="true"/>
Per disabiliare la modalità di debug, occorre chiamare setDebug(false)
. Se si vogliono scrivere dei messaggi propri nel log di debug, occorre chiamare Seam.Remoting.log(message)
.
Quando si fa una chiamata remota, è possibile specificare un gestore di eccezioni che processerà la risposta nell'eventualità che il metodo lanci un'eccezione. Per specificare una funzione di gestione delle eccezioni, bisogna includere nel Javascript un riferimento ad essa dopo il parametro di callback:
var callback = function(result) { alert(result); }; var exceptionHandler = function(ex) { alert("An exception occurred: " + ex.getMessage()); }; Seam.Component.getInstance("helloAction").sayHello(name, callback, exceptionHandler);
Se non viene indicato il gestore della callback, al suo posto bisogna specificare null
:
var exceptionHandler = function(ex) { alert("An exception occurred: " + ex.getMessage()); }; Seam.Component.getInstance("helloAction").sayHello(name, null, exceptionHandler);
L'oggetto eccezione che viene passato al gestore espone un solo metodo, getMessage()
, che restituisce il messaggio di errore prodotto dall'eccezione lanciata dal memtodo annotato con @WebRemote
.
Il messaggio di caricamento predefinito che appare nell'angolo dello schermo in alto a destra può essere modificato e il suo rendering personalizzato o anche eliminato completamente.
Per cambiare il messaggio dal default "Attendere prego..." a qualcosa di differente, si imposti il valore di Seam.Remoting.loadingMessage
:
Seam.Remoting.loadingMessage = "Loading...";
Per sopprimere il messaggio di caricamento, occorre fare l'override dell'implementazione di displayLoadingMessage()
e hideLoadingMessage()
con funzioni che non fanno nulla:
// non mostrare l'indicatore di caricamento
Seam.Remoting.displayLoadingMessage = function() {};
Seam.Remoting.hideLoadingMessage = function() {};
E' anche possibile sovrascrivere l'indicatore di caricamento per mostrare un'icona animata o qualunque altra cosa si voglia. A questo scopo occorre fare l'override di displayLoadingMessage()
e hideLoadingMessage()
con un'implementazione propria:
Seam.Remoting.displayLoadingMessage = function() {
// Write code here to display the indicator
};
Seam.Remoting.hideLoadingMessage = function() {
// Write code here to hide the indicator
};
Quando un metodo remoto viene esegutio, il risultato viene serializzato in una risposta XML che viene restituita al client. Questa risposta viene allora deserializzata dal client in un oggetto Javascript. Nel caso di tipi complessi (vedi JavaBean) che includono riferimenti ad altri oggetti, anche tutti gli oggetti referenziati vengono serializzati all'interno della risposta. Questi oggetti possono referenziarne altri, che possono referenziarne altri ancora e così via. Se non lo si controlla, questo "grafo" di oggetti potrebbe essere potenzialmente enorme, a seconda delle relazioni esistenti tra gli oggetti stessi. E vi è l'ulteriore problema (oltre alla potenziale verbosità della risposta) che potrebbe essere desiderabile impedire che delle informazioni sensibili vengano esposte al client.
Seam Remoting fornisce un modo semplice per "vincolare" il grafo di oggetti: basta valorizzare il campo exclude
dell'annotazione @WebRemote
del metodo remoto. Questo campo accetta un array di stringhe contenenti uno o più percorsi specificati usando la notazione col punto. Quando un metodo remoto viene invocato, gli oggetti del grafo risultante il cui percorso coincide con uno di questi vengono esclusi dalla serializzazione.
Per tutti gli esempi utilizzeremo la seguente classe Widget
:
@Name("widget")
public class Widget
{
private String value;
private String secret;
private Widget child;
private Map<String,Widget> widgetMap;
private List<Widget> widgetList;
// getters and setters for all fields
}
Se il metodo remoto restituisce un'istanza di Widget
, ma non si volesse esporre il campo secret
poiché contiene delle informazioni sensibili, sarebbe possibile escluderlo in questo modo:
@WebRemote(exclude = {"secret"})
public Widget getWidget();
Il valore "secret" si riferisce al campo secret
dell'oggetto restituito. Ora, supponiamo che non ci importi di esporre questo campo particolare al client. Invece, si noti che il valore Widget
che viene restituito ha un campo figlio
che pure è un Widget
. Cosa succede se, invece, si vuole nascondere il valore secret
del figlio
? Possiamo raggiungere lo scopo usando la notazione col punto per indicare il percorso di questo campo all'interno del grafo di oggetti risultante:
@WebRemote(exclude = {"child.secret"})
public Widget getWidget();
L'altro luogo dove degli oggetti possono esistere all'interno di un grafo sono oggetti di tipo Map
o qualche genere di collezione (List
, Set
, Array
, etc). Le collezioni sono semplici e trattate come qualunque altro campo. Per esempio, se Widget
contenesse una lista di altri Widget
nel suo campo widgetList
, per escludere il campo secret
dei Widget
di questa lista l'annotazione avrebbe questo aspetto:
@WebRemote(exclude = {"widgetList.secret"})
public Widget getWidget();
Per vincolare la chiave o il valore di un oggetto Map
, la notazione è leggermente diversa. Aggiungere [key]
dopo il nome del campo di tipo Map
vincolerà il valore delle chiavi del campo di tipo Map
, mentre [value]
vincolerà il valore dei valori del campo di tipo Map
. Gli esempi seguenti mostrano come i valori del campo widgetMap
si trovino ad avere il campo secret
vincolato:
@WebRemote(exclude = {"widgetMap[value].secret"})
public Widget getWidget();
Vi è un'ultima notazione che può essere usata per vincolare i campi di un tipo di oggetto, indipendentemente da dove appare all'interno del grafo di oggetti restituito. Questa notazione utilizza o il nome del componente (se l'oggetto è un componente Seam) o il nome completo della classe (soltanto se l'oggetto non è un componente Seam) ed viene costruita usando le parentesi quadre:
@WebRemote(exclude = {"[widget].secret"})
public Widget getWidget();
Di default non vi è alcuna transazione attiva durante una richiesta remota, così che, se si vogliono apportare delle modifiche al database, è necessario annotare il metodo @WebRemote
con @Transactional
, come di seguito:
@WebRemote @Transactional(TransactionPropagationType.REQUIRED) public void updateOrder(Order order) { entityManager.merge(order); }
Seam Remoting fornisce supporto sperimentale per la gestione di messaggi JMS. Questa sezione descrive il tipo di supporto attualmente implementato, ma si tenga presente che esso potrà cambiare in futuro. Attualmente si raccomanda di non usare tale funzionalità in un ambiente di produzione.
Prima di sottoscrivere una topic JMS, occorre configurare la lista di topic che Seam Remoting può sottoscrivere. Occorre elencare i topic nella proprietà org.jboss.seam.remoting.messaging.subscriptionRegistry.allowedTopics
in seam.properties
, web.xml
o components.xml
.
<remoting:remoting poll-timeout="5" poll-interval="1"/>
L'esempio seguente mostra come sottoscrivere un topic JMS:
function subscriptionCallback(message)
{
if (message instanceof Seam.Remoting.TextMessage)
alert("Received message: " + message.getText());
}
Seam.Remoting.subscribe("topicName", subscriptionCallback);
Il metodo Seam.Remoting.subscribe()
accetta due parametri, di cui il primo è il nome del topic JMS da sottoscrivere e il secondo la funzione di callback che deve essere invocata quando si riceve un messaggio.
Due sono i tipi di messaggi supportati, messaggi testuali e messaggi oggetto. Per verificare il tipo di messaggio passato alla callback è possibile usare l'operatore instanceof
per vedere se il messaggio sia un Seam.Remoting.TextMessage
o un Seam.Remoting.ObjectMessage
. Un TextMessage
contiene il valore testuale nel campo text
(si può anche fare una chiamata al metodo getText()
), mentre un ObjectMessage
contiene il valore oggetto nel campo value
(si può anche fare una chiamata al metodo getValue()
).
Per annullare la sottoscrizione ad un topic, bisogna fare una chiamata a Seam.Remoting.unsubscribe()
passando il nome del topic come parametro:
Seam.Remoting.unsubscribe("topicName");
E' possibile controllare il polling dei messaggi attraverso due parametri. Il primo è Seam.Remoting.pollInterval
, che controlla quanto tempo passa tra due polling successivi per verificare l'arrivo di nuovi messaggi. Tale parametro è espresso in secondi, e il suo valore predefinito è 10.
Il secondo parametro è Seam.Remoting.pollTimeout
, che pure è espresso in secondi. Controlla per quanto tempo una richiesta al server debba stare in attesa di un nuovo messaggio prima di andare in time out e inviare una risposta vuota. Il valore predefinito è 0: ciò significa che quando il server viene interrogato, se non ci sono nuovi messaggi pronti alla consegna, allora viene immediatamente restituita una risposta vuota.
Occorre essere molto cauti nell'impostare un valore alto di pollTimeout
; ogni richiesta in attesa di un messaggio tiene un thread attivo finché non viene ricevuto un messaggio o finché la richiesta non va in time out. Se molte di queste richieste devono essere gestite contemporaneamente, si potrebbero avere molti thread in attesa.
Si raccomanda si impostare queste opzioni in components.xml, anche se possono essere sovrascritte dal codice Javascript. L'esempio seguente mostra come configurare il polling in modo da renderlo molto aggressivo. Dovreste impostare questi parametri a dei valori adatti alla vostra applicazione:
Via components.xml:
<remoting:remoting poll-timeout="5" poll-interval="1"/>
Via JavaScript:
// Attendere 1 secondo tra la ricezione della risposta del pool e l'invio della successiva richiesta di pool.
Seam.Remoting.pollInterval = 1;
// Attendere fino a 5 secondi sul server per nuovi messaggi
Seam.Remoting.pollTimeout = 5;