SeamFramework.orgCommunity Documentation

Web Beans: Contesti Java e Dependency Injection

Il nuovo standard Java per la dependency injection e la gestione contestuale dello stato


Nota
I. Usare gli oggetti contestuali
1. Iniziare con Web Beans
1.1. Il primo Web Bean
1.2. Cosa è un Web Bean?
1.2.1. Tipi di API, tipi di binding e dependency injection
1.2.2. Tipi di deploy
1.2.3. Scope
1.2.4. Nomi Web Bean e Unified EL
1.2.5. Tipi di interceptor binding
1.3. Quali tipi di oggetti possono essere Web Beans?
1.3.1. Web Beans Semplici
1.3.2. Web Bean Enterprise
1.3.3. Metodi produttori
1.3.4. Endpoint JMS
2. Esempio di applicazione web JSF
3. Implementazione di riferimento di Web Beans
3.1. Esempio Indovina Numero
3.2. Esempio Traduttore
4. Dependency injection
4.1. Annotazioni di binding
4.1.1. Annotazioni di binding con membri
4.1.2. Combinazioni di binding annotation
4.1.3. Binding annotation e metodi produttori
4.1.4. Il tipo di binding predefinito
4.2. Tipi di deploy
4.2.1. Abilitazione dei tipi di deploy
4.2.2. Precedenza del tipo di deploy
4.2.3. Esempio dei tipi di deploy
4.3. Risoluzione di dipendenze non soddisfatte
4.4. Client proxy
4.5. Ottenere un riferimento a un Web Bean via codice
4.6. Chiamare al ciclo di vita, @Resource, @EJB e @PersistenceContext
4.7. L'oggetto InjectionPoint
5. Scope e contesti
5.1. Tipi di scope
5.2. Scope predefiniti
5.3. Lo scope conversazione
5.3.1. Demarcazione della conversazione
5.3.2. Propagazione della conversazione
5.3.3. Timeout della conversazione
5.4. Pseudo-scope dipendente
5.4.1. Annotazione @New
6. Metodi produttori
6.1. Scope di un metodo produttore
6.2. Iniezione nei metodi produttori
6.3. Uso di @New con i metodi produttori
II. Sviluppare codice debolmente-accoppiato
7. Gli interceptor
7.1. Interceptor bindings
7.2. Implementare gli interceptor
7.3. Abilitare gli interceptor
7.4. Interceptor binding con membri
7.5. Annotazioni per interceptor binding multipli
7.6. Ereditarietà del tipo di interceptor binding
7.7. Uso di @Interceptors
8. Decoratori
8.1. Attributi delegate
8.2. Abilitare i decoratori
9. Eventi
9.1. Osservatori di eventi
9.2. Produttori di eventi
9.3. Registrare dinamicamente gli osservatori
9.4. Event binding con membri
9.5. Event binding multipli
9.6. Osservatori transazionali
III. Realizzare una tipizzazione più forte
10. Stereotipi
10.1. Scope di default e tipo di deploy per uno stereotipo
10.2. Restringere lo scope ed il tipo con uno stereotipo
10.3. Interceptor binding per gli stereotipi
10.4. Assegnare nomi di default con gli stereotipi
10.5. Stereotipi standard
11. Specializzazione
11.1. Usare la specializzazione
11.2. Vantaggi della specializzazione
12. Definire i Web Beans tramite XML
12.1. Dichiarare classi Web Bean
12.2. Dichiarare metadati Web Bean
12.3. Dichiarare membri Web Bean
12.4. Dichiarazione inline dei Web Beans
12.5. Uso di uno schema
IV. Web Beans e l'ecosistema Java EE
13. Integrazione Java EE
13.1. Iniettare risorse Java EE in un Web Bean
13.2. Chiamare un Web Bean da un servlet
13.3. Chiamare un Web Bean da un Message-Driven Bean
13.4. Endpoint JMS
13.5. Packaging and deployment
14. Estendere i Web Beans
14.1. L'oggetto Manager
14.2. La classe Bean
14.3. L'interfaccia Context
15. Prossimi passi
A. Integrazione di Web Beans RI in altri ambienti
A.1. Web Beans RI SPI
A.1.1. Web Bean Discovery
A.1.2. EJB Discovery
A.1.3. @EJB, @PersistenceContext e @Resource resolution
A.1.4. Transaction Services
A.1.5. Il contesto applicazione
A.1.6. Bootstrap e spegnimento
A.1.7. JNDI
A.1.8. Caricamento risorse
A.1.9. Iniezione dei servlet
A.2. Il contratto con il container

JSR-299 ha recentemente cambiato il suo nome da "Web Beans" a "Contesti Java e Dependency Injection". La guida fa comunque riferimento alla JSR-299 come "Web Beans" e alla JSR-299 Reference Implementation come "Web Beans RI". Altre documentazioni, blogs, forum, ecc. potrebbero usare la nuova nomenclatura, includendo il nuovo nome per la JSR-299 Reference Implementation - "Web Beans".

You'll also find that some of the more recent functionality to be specified is missing (such as producer fields, realization, asynchronous events, XML mapping of EE resources).

La specifica Web Beans (JSR-299) definisce un insieme di servizi per l'ambiente Java EE che rende molto più facile lo sviluppo di applicazioni. Web Beans sovrappone un più ricco modello di interazione e di gestione del ciclo di vita ai tipi di componenti java esistenti, Java Beans and Enterprise Java Beans inclusi. A complemento del tradizionale modello di programmazione Java EE, i servizi Web Beans forniscono:

La dependency injection, insieme alla gestione contestuale del ciclo di vita dei componenti, risparmia a chi utilizza un API con cui non ha familiarità la necessità di dover formulare le risposte relative alle seguenti domande:

Un Web Bean specifica soltanto il tipo e la semantica degli altri Web Beans da cui dipende. Non ha bisogno di essere a conoscenza del reale ciclo di vita, della implementazione, del modello di threading o degli altri client dei Web Bean da cui dipende. Ancor meglio, l'implementazione, il ciclo di vita e il modello di threading di un Web Bean da cui dipende possono variare a seconda dello scenario di deployment, senza avere effetti su nessun client.

Eventi, interceptor e decoratori potenziano l'accoppiamento debole (loose-coupling) inerente a questo modello:

Soprattutto, Web Beans fornisce tutti questi strumenti in un modo typesafe. Web Beans non usa mai identificatori di tipo stringa per determinare come interagiscono oggetti che collaborano fra di loro. Sebbene resti un'opzione, il linguaggio XML è usato raramente. Invece Web Beans utilizza l'informazione di tipo già presente nel modello a oggetti di Java, insieme ad un nuovo pattern, chiamato binding annotations, per assemblare i Web Beans, le loro dipendenze, i loro interceptor e decoratori e i loro consumatori di eventi.

I servizi di Web Beans sono generali e applicabili ai seguenti tipi di componenti che esistono in ambiente Java EE:

Web Beans fornisce anche i necessari punti di integrazione in modo che altri tipi di componenti definiti da future specifiche Java EE o da framework non standard possano essere integrati in modo trasparente con Web Beans, avvantaggiarsi dei suoi servizi, e interagire con qualunque altro tipo di Web Bean.

Web Beans è stata influenzata da un buon numero di framework Java esistenti, inclusi Seam, Guice and Spring. Comunque, Web Beans ha un proprio chiaro carattere distintivo: è più sicuro nell'uso dei tipi (typesafe) di Seam, è più orientato allo stato (stateful) e meno basato su XML di Spring, e più capace di Guice nelle applicazioni web ed enterprise.

Soprattutto, Web Beans è uno standard JCP che si integra in modo trasparente con Java EE, e con qualunque ambiente Java SE dove EJB Lite sia disponibile in modo embeddable.

Non vedi l'ora di iniziare a scrivere il primo Web Beans? O forse sei un pò scettico e ti domandi quali virtuosismi ti farà fare la specifica Web Beans! La buona notizia è che probabilmente hai già scritto e usato centinaia, forse migliaia di Web Beans. Potresti addirittura non ricordare il primo Web Bean scritto.

Con alcune eccezioni molto particolari, ogni classe Java con un costruttore che non accetta parametri è un Web Bean. Questo include ogni JavaBean. Inoltre, ogni session bean di stile EJB3 è un Web Bean. Sicuramente i JavaBean e gli EJB3 che si sono sempre scritti non erano in grado di sfruttare i nuovi servizi definiti dalla specifica Web Beans, ma si sarà presto in grado di usare ciascuno di essi come Web Bean — iniettandoli in altri Web Beans, configurandoli tramite strumenti di configurazione XML Web Bean, e perfino aggiungendo a loro interceptor e decoratori — senza toccare il codice esistente.

Si supponga di avere due classi Java esistenti, usate da anni in varie applicazioni. La prima classe esegue il parsing di una stringa in una lista di frasi:

public class SentenceParser {

    public List<String
> parse(String text) { ... }
}

La seconda classe è un session bean stateless front-end per un sistema esterno capace di tradurre le frasi da una lingua ad un altra:

@Stateless

public class SentenceTranslator implements Translator {
    public String translate(String sentence) { ... }
}

Dove Translator è l'interfaccia locale:

@Local

public interface Translator {
    public String translate(String sentence);
}

Sfortunatamente non ci sono classi preesistenti che traducano l'intero testo dei documenti. Quindi occorre scrivere un Web Bean che faccia questo lavoro:

public class TextTranslator {

    
    private SentenceParser sentenceParser;
    private Translator sentenceTranslator;
    
    @Initializer
    TextTranslator(SentenceParser sentenceParser, Translator sentenceTranslator) {
        this.sentenceParser = sentenceParser;
        this.sentenceTranslator = sentenceTranslator;
    }
    
    public String translate(String text) {
        StringBuilder sb = new StringBuilder();
        for (String sentence: sentenceParser.parse(text)) {
            sb.append(sentenceTranslator.translate(sentence));
        }
        return sb.toString();
    }
    
}

Si può ottenere un'istanza di TextTranslator iniettandolo in un Web Bean, Servlet o EJB:

@Initializer

public setTextTranslator(TextTranslator textTranslator) {
    this.textTranslator = textTranslator;
}

In alternativa si può ottenere un'istanza chiamando direttamente un metodo del manager Web Bean:

TextTranslator tt = manager.getInstanceByType(TextTranslator.class);

Ma TextTranslator non ha un costruttore con nessun parametro! E' ancora un Web Bean? Una classe che non ha un costruttore senza parametri può essere un Web Bean se il suo costruttore è annotato con @Initializer.

Come hai indovinato, l'annotazione @Initializer ha qualcosa che fare con la dependency injection! @Initializer può essere applicato ad un costruttore od un metodo di un Web Bean, e dice al manager Web Bean di chiamare quel costruttore o metodo quando si istanzia il Web Bean. Il manager Web Bean inietterà altri Web Bean nei parametri del costruttore o del metodo.

In fase di inizializzazione del sistema, il manager Web Bean deve convalidare che esattamente un solo Web Bean esista e soddisfi ciascun punto di iniezione. Nell'esempio, se nessuna implementazione di Translator fosse disponibile — se l'EJB SentenceTranslator non venisse deployato — il manager Web Bean lancerebbe una UnsatisfiedDependencyException. Se più di un'implementazione di Translator fosse disponibile, il manager Web Bean lancerebbe una AmbiguousDependencyException.

Ma cosa è esattamente un Web Bean?

Un Web Bean è una classe di un'applicazione che contiene della logica di business. Può essere chiamato direttamente da codice Java, o può essere invocato via Unified EL. Un Web Bean può accedere a risorse transazionali. Le dipendenze tra Web Beans sono gestite automaticamente dal manager Web Bean. La maggior parte dei Web Beans sono stateful e contestuali. Il ciclo di vita di un Web Bean è sempre gestito da un manager Web Bean.

Torniamo indietro un attimo. Cosa significa veramente essere "contestuale"? Poiché Web Beans può essere stateful, è importante quale istanza di bean si ha. Diversamente da un modello a componenti stateless (per esempio, i session bean stateless) o un modello a componenti singleton (come i servlet o i bean singleton) i client di un Web Bean vedono il Web Bean in stati differenti. Lo stato del client visibile dipende dall'istanza del Web Bean alla quale il client ha il riferimento.

Comunque, in modo simile ad un modello stateless o singleton, ma non come i session bean stateful, il client non ha il controllo sul ciclo di vita dell'istanza, creandola e distruggendola esplicitamente. Invece, lo scope del Web Bean determina:

Per un dato thread in un'applicazione Web Beans, ci può essere un contesto attivo associato allo scope del Web Bean. Questo contesto può essere univoco nel thread (per esempio, se il Web Bean è con scope di richiesta), o può essere condiviso con alcuni altri thread (per esempio, se il Web Bean è con scope di sessione) od anche tutti gli altri thread (se è scope di applicazione).

I client (per esempio, altri Web Beans) che sono in esecuzione nello stesso contesto vedranno la stessa istanza del Web Bean. Ma i client in un contesto diverso vedranno un istanza diversa.

Un grande vantaggio del modello contestuale è che consente ai Web Beans stateful di essere trattati come servizi! Il client non deve preoccuparsi di gestire il ciclo di vita del Web Bean che sta utilizzando, e neppure deve sapere quale sia il ciclo di vita. Web Beans interagisce passando i messaggi, e le implementazioni Web Bean definiscono il ciclo di vita del proprio stato. I Web Beans sono debolmente disaccoppiati (loosely coupled) poiché:

Si può sostituire un Web Bean con un diverso Web Bean che implementa la stessa API ed ha un diverso ciclo di vita (un diverso scope) senza alterare l'implementazione dell'altro Web Bean. Infatti Web Beans definisce un meccanismo sofisticato per fare l'override delle implementazioni Web Bean al momento del deploy, come visto in Sezione 4.2, «Tipi di deploy».

Si noti che non tutti i client dei un Web Bean sono Web Bean. Altri oggetti come Servlet o Message-Driven Beans — che sono per natura non iniettabili, oggetti contestuali — possono pure ottenere riferimenti a Web Bean tramite iniezione.

Più formalmente, secondo la specifica:

Un Web Bean comprende:

  • Un set (non vuoto) di tipi di API

  • Un set (non vuoto) di tipi di annotazione di binding

  • Uno scope

  • Un tipo di deploy

  • Opzionalmente un nome Web Bean

  • Un set di tipi di interceptor binding

  • Un implementazione Web Bean

Vediamo cosa significano alcuni di questi termini per lo sviluppatore Web Bean.

I Web Bean solitamente acquisiscono riferimenti ad altri Web Bean tramite la dependency injection. Ogni attributo iniettato specifica un "contratto" che deve essere soddisfatto dal Web Bean per essere iniettato. Il contratto è:

Un API è una classe o interfaccia definita dall'utente. (Se il Web Bean è un session bean EJB, il tipo di API è l'interfaccia @Local o la vista locale della classe-bean). Un tipo di binding rappresenta un semantica del client che è soddisfatta da certe implementazioni dell'API e non da altre.

I tipi di binding sono rappresentati da annotazioni definite dall'utente che sono loro stesse annotate con @BindingType. Per esempio, il seguente punto di iniezione ha un tipo di API PaymentProcessor ed un tipo di binding @CreditCard:

@CreditCard PaymentProcessor paymentProcessor

Se nessun tipo di binding viene specificato in modo esplicito ad un punto di iniezione, il tipo di binding di default si assume essere @Current.

Per ogni punto di iniezione, il manager Web Bean cerca un Web Bean che soddisfi il contratto (che implementi la API, e che abbia tutti i tipi di binding), ed inietta tale Web Bean.

Il seguente Web Bean ha il tipo binding @CreditCard e implementa il tipo API PaymentProcessor. Può quindi essere iniettato nel punto di iniezione d'esempio:

@CreditCard

public class CreditCardPaymentProcessor 
    implements PaymentProcessor { ... }

Se un Web Bean non specifica esplicitamente un set di tipi di binding, ha esattamente un solo tipo di binding: il tipo di binding di default @Current.

Web Beans definisce un algoritmo di risoluzione sofisticato ma intuitivo che aiuta il container a decidere cosa fare se più di un Web Bean soddisfa un particolare contratto. Vedremo i dettagli in Capitolo 4, Dependency injection.

Si è già visto che JavaBeans, EJB ed altri tipi di classi Java possono essere Web Bean. Ma esattamente quali tipi di oggetti sono Web Beans?

La specifica dice che tutti i bean di sessione stile EJB3 e quelli singleton sono Web Bean enterprise. I bean message driven non sono Web Bean — poiché non sono intesi per essere iniettati in altri oggetti — ma possono sfruttare la maggior parte della funzionalità dei Web Bean, inclusi dependency injection e interceptor.

Ogni interfaccia locale di un Web Bean enterprise che non ha un parametro tipo wildcard o variabile tipo, e ciascuna delle sue superinterfacce, è un tipo di API del Web Bean enterprise. Se il bean EJB ha una vista locale di classe bean, la classe bean e ogni sua superclasse è anch'essa un tipo di API.

I session bean stateful dovrebbero dichiarare un metodo remoto senza parametri od un metodo annotato con @Destructor. Il manager Web Bean chiama questo metodo per distruggere l'istanza del session bean statefull alla fine del suo ciclo di vita. Questo metodo è chiamato metodo distruttore del Web Bean enterprise.

@Stateful @SessionScoped

public class ShoppingCart {
    ...
    
    @Remove
    public void destroy() {}
}

Ma allora quando occorre usare un Web Bean enterprise invece di un Web Bean semplice? Quando occorrono servizi enterprise avanzati offerti da EJB, quali:

si dovrebbe usare un Web Bean enterprise. Quando non occorrono queste cose, va bene utilizzare un Web Bean semplice.

Molti Web Bean (inclusi Web Bean con scope di sessione o applicazione) sono disponibili per accessi concorrenti. Quindi la gestione della concorrenza fornita da EJB3.1 è molto utile. La maggior parte dei Web Bean con scope sessione e applicazione dovrebbero essere EJB.

Web Bean che mantengono riferimenti alle risorse pesanti o mantengono molti benefici dello stato interno dal ciclo di vita avanzato, gestito dal container, definito dal modello EJB @Stateless/@Stateful/@Singleton", con supporto alla passivazione e pooling delle istanze.

Infine è ovvio quando occorre usare la gestione delle transazioni a livello di metodo, la sicurezza a livello di metoto, i timer, i metodi remoti o i metodi asincroni.

E' facile iniziare con un Web Bean semplice e poi volgere a EJB semplicemente aggiungendo l'annotazione: @Stateless, @Stateful o @Singleton.

Illustriamo queste idee con un esempio completo. Implementiamo il login/logout dell'utente per un'applicazione che utilizza JSF. Innanzitutto definiamo un Web Bean che mantenga username e password digitati durante il login:

@Named @RequestScoped

public class Credentials {
        
    private String username;
    private String password;
    
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }
    
}

Questo Web Bean è associato al login all'interno della seguente form JSF:

<h:form>

    <h:panelGrid columns="2" rendered="#{!login.loggedIn}">
        <h:outputLabel for="username"
>Username:</h:outputLabel>
        <h:inputText id="username" value="#{credentials.username}"/>
        <h:outputLabel for="password"
>Password:</h:outputLabel>
        <h:inputText id="password" value="#{credentials.password}"/>
    </h:panelGrid>
    <h:commandButton value="Login" action="#{login.login}" rendered="#{!login.loggedIn}"/>
    <h:commandButton value="Logout" acion="#{login.logout}" rendered="#{login.loggedIn}"/>
</h:form
>

Il vero lavoro è fatto da un Web Bean con scope di sessione che mantiene le informazioni sull'utente correntemente loggato ed espone l'entity User agli altri Web Beans:

@SessionScoped @Named

public class Login {
    @Current Credentials credentials;
    @PersistenceContext EntityManager userDatabase;
    private User user;
    
    public void login() {
            
        List<User
> results = userDatabase.createQuery(
           "select u from User u where u.username=:username and u.password=:password")
           .setParameter("username", credentials.getUsername())
           .setParameter("password", credentials.getPassword())
           .getResultList();
        
        if ( !results.isEmpty() ) {
           user = results.get(0);
        }
        
    }
    
    public void logout() {
        user = null;
    }
    
    public boolean isLoggedIn() {
       return user!=null;
    }
    
    @Produces @LoggedIn User getCurrentUser() {
        return user;
    }
}

@LoggedIn è un'annotazione di binding:

@Retention(RUNTIME)

@Target({TYPE, METHOD, FIELD})
@BindingType
public @interface LoggedIn {}

Ora qualsiasi altro Web Bean può facilmente iniettare l'utente corrente:

public class DocumentEditor {


    @Current Document document;
    @LoggedIn User currentUser;
    @PersistenceContext EntityManager docDatabase;
    
    public void save() {
        document.setCreatedBy(currentUser);
        docDatabase.persist(document);
    }
    
}

Quest'esempio è un assaggio del modello di programmazione con Web Bean. Nel prossimo capitolo esploreremo la dependency injection dei Web Bean con maggior profondità.

La Web Beans Reference Implementation viene sviluppata all'indirizzo the Seam project. Si può scaricare l'ultima release di Web Beans dalla pagina di download.

La Web Beans RI (Reference Implementation) viene distribuita con due applicazioni deployabili d'esempio, webbeans-numberguess, un esempio in formato war, che contiene solo bean semplici, e webbeans-translator, un esempio in formato ear, che contiene bean enterprise. Per eseguire gli esempi occorre fare le seguenti cose:

  • l'ultima release di Web Beans RI,

  • JBoss AS 5.0.0.GA, and

  • Ant 1.7.0.

Currently, the Web Beans RI only runs on JBoss Application Server 5. You'll need to download JBoss AS 5.0.1.GA from jboss.org, and unzip it. For example:

$ cd /Applications
$ unzip ~/jboss-5.0.1.GA.zip

Scaricare Web Beans RI da seamframework.org, e scompattarla. Per esempio

$ cd ~/
$ unzip ~/webbeans-$VERSION.zip

Quindi, occorre indicare a Web Beans dove è stato installato JBoss. Modificate il file jboss-as/build.properties e valorizzate la proprietà jboss.home. Per esempio:

jboss.home=/Applications/jboss-5.0.1.GA

Nota

A new deployer, webbeans.deployer is added to JBoss AS. This adds supports for Web Bean archives to JBoss AS, and allows the Web Beans RI to query the EJB3 container and discover which EJBs are installed in your application.

Web Beans is bundled with JBoss AS 5.1 and above.

To install Web Beans, you'll need Ant 1.7.0 installed, and the ANT_HOME environment variable set. For example:

$ unzip apache-ant-1.7.0.zip
$ export ANT_HOME=~/apache-ant-1.7.0

Then, you can install the update. The update script will use Maven to download Web Beans automatically.

$ cd webbeans-$VERSION/jboss-as
$ ant update

Ora, siete pronti a fare il deploy del primo esempio!

Suggerimento

Gli script di build degli esempio offrono una quantità di target, cioè:

  • ant restart - fa il deploy dell'esempio in formato esploso

  • ant explode - aggiorna un esempio in formato esploso, senza riavviare il deploy

  • ant deploy - fa il deploy dell'esempio in formato jar compresso

  • ant undeploy - rimuove l'esempio dal server

  • ant clean - ripulisce l'esempio

Per fare il deploy dell'esempio Indovina Numero:

$ cd examples/numberguess
ant deploy

Avviare JBoss AS:

$ /Application/jboss-5.0.0.GA/bin/run.sh

Suggerimento

Se si usa Windows, si usi lo script run.bat.

Attendete che l'applicazione sia installata, e godetevi ore di divertimento all'indirizzo http://localhost:8080/webbeans-numberguess!

La RI di Web Beans include un secondo semplice esempio che tradurrà i vostri testi in Latino. L'esempio Indovina Numero è in formato war, e usa soltanto bean semplici; l'esempio col traduttore è in formato ear, e include dei bean enterprise, assemblati in un modulo EJB. Per provarlo:

$ cd examples/translator
ant deploy

Attendete che l'applicazione sia installata, e visitate l'indirizzo http://localhost:8080/webbeans-translator!

Nell'applicazione Indovina Numero avete a disposizione 10 tentativi per indovinare un numero tra 1 e 100. Dopo ciascun tentativo, siete informati se siete stati troppo alti o troppo bassi.

L'esempio Indovina Numero comprende un certo numero di Web Bean, file di configurazione e pagine JSF, assemblati in un war. Iniziamo dai file di configurazione.

Tutti i file di configurazione di questo esempio si trovano in WEB-INF/, che è situato in WebContent nell'albero dei sorgenti. Innanzitutto, c'è faces-config.xml, in cui JSF viene informata di usare Facelets:


<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="1.2"
              xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
    
    <application>
        <view-handler
>com.sun.facelets.FaceletViewHandler</view-handler>
    </application>

</faces-config
>

Vi è un file vuoto web-beans.xml, che identifica l'applicazione come applicazione Web Beans.

Infine c'è web.xml:

Let's take a look at the Facelet view:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:s="http://jboss.com/products/seam/taglib">

  <ui:composit(1)ion template="template.xhtml">
    <ui:define name="content">
       <h1
>Guess a number...</h1>
       <h:form(2) id="NumberGuessMain">
          <div style="color: red">
             <h:messages id="messages" globalOnly="false"/>
             <h:outputText id="Higher" value="Higher!" rendered="#{game.number gt game.guess and game.guess ne 0}"/>
             <h:outputText id="Lower" value="Lower!" rendered="#{game.number lt game.guess and game.guess ne 0}"/>
          </div>
   
          <div(3)>
             I'm thinking of a number between #{game.smallest} and #{game.biggest}.
             You have #{game.remainingGuesses} guesses.
          </div>
     
          <div>
             Y(4)our guess: 
             <h:inputText id="inputGuess" 
                          value="#{game.guess}" 
                          required="true" 
                          size="3" 
              (5)            disabled="#{game.number eq game.guess}">
                <f:validateLongRange maximum="#{game.biggest}" 
                                     minimum="#{game.smallest}"/>
             <(6)/h:inputText>
            <h:commandButton id="GuessButton"  
                             value="Guess" 
                             action="#{game.check}" 
                             disabled="#{game.number eq game.guess}"/>
          </div>
          <div>
            <h:commandButton id="RestartButton" value="Reset" action="#{game.reset}" immediate="true" />
          </div>
       </h:form>
    </ui:define>
  </ui:composition>
</html
>
1

Facelets is a templating language for JSF, here we are wrapping our page in a template which defines the header.

2

There are a number of messages which can be sent to the user, "Higher!", "Lower!" and "Correct!"

3

As the user guesses, the range of numbers they can guess gets smaller - this sentance changes to make sure they know what range to guess in.

4

This input field is bound to a Web Bean, using the value expression.

5

A range validator is used to make sure the user doesn't accidentally input a number outside of the range in which they can guess - if the validator wasn't here, the user might use up a guess on an out of range number.

6

And, of course, there must be a way for the user to send their guess to the server. Here we bind to an action method on the Web Bean.

L'esempio consiste di 4 classi, delle quali le prime due sono tipi di binding. Innanzitutto, c'è il tipo di binding @Random, usato per iniettare un numero casuale:

@Target( { TYPE, METHOD, PARAMETER, FIELD })

@Retention(RUNTIME)
@Documented
@BindingType
public @interface Random {}

C'è anche il tipo di binding @MaxNumber, usato per iniettare il numero massimo iniettatabile:

@Target( { TYPE, METHOD, PARAMETER, FIELD })

@Retention(RUNTIME)
@Documented
@BindingType
public @interface MaxNumber {}

Alla classe Generator è affidata la generazione del numero casuale, per mezzo di un metodo produttore. Inoltre essa espone il massimo numero possibile attraverso un metodo produttore:

@ApplicationScoped

public class Generator {
   
   private java.util.Random random = new java.util.Random( System.currentTimeMillis() );
   
   private int maxNumber = 100;
   
   java.util.Random getRandom()
   {
      return random;
   }
   
   @Produces @Random int next() { 
      return getRandom().nextInt(maxNumber); 
   }
   
   @Produces @MaxNumber int getMaxNumber()
   {
      return maxNumber;
   }
}

E' possibile notare che Generator ha scope applicazione; quindi non si ottiene un diverso numero casuale ogni volta.

Il Web Bean finale nell'applicazione è Game avente scope di sessione.

Si noti anche che è stata usata l'annotazione @Named, in modo che sia possibile usare il bean in espressioni EL presenti nelle pagine JSF. Infine, si è utilizzata l'iniezione del costruttore per inizializzare il gioco con un numero casuale. E naturalmente, è necessario dire al giocatore se ha vinto, informazione di feedback che viene fornita con un FacesMessage.

package org.jboss.webbeans.examples.numberguess;



import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.webbeans.AnnotationLiteral;
import javax.webbeans.Current;
import javax.webbeans.Initializer;
import javax.webbeans.Named;
import javax.webbeans.SessionScoped;
import javax.webbeans.manager.Manager;
@Named
@SessionScoped
public class Game
{
   private int number;
   
   private int guess;
   private int smallest;
   private int biggest;
   private int remainingGuesses;
   
   @Current Manager manager;
   
   public Game()
   {
   }
   
   @Initializer
   Game(@MaxNumber int maxNumber)
   {      
      this.biggest = maxNumber;
   }
   public int getNumber()
   {
      return number;
   }
   
   public int getGuess()
   {
      return guess;
   }
   
   public void setGuess(int guess)
   {
      this.guess = guess;
   }
   
   public int getSmallest()
   {
      return smallest;
   }
   
   public int getBiggest()
   {
      return biggest;
   }
   
   public int getRemainingGuesses()
   {
      return remainingGuesses;
   }
   
   public String check()
   {
      if (guess
>number)
      {
         biggest = guess - 1;
      }
      if (guess<number)
      {
         smallest = guess + 1;
      }
      if (guess == number)
      {
         FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Correct!"));
      }
      remainingGuesses--;
      return null;
   }
   
   @PostConstruct
   public void reset()
   {
      this.smallest = 0;
      this.guess = 0;
      this.remainingGuesses = 10;
      this.number = manager.getInstanceByType(Integer.class, new AnnotationLiteral<Random
>(){});
   }
   
}

L'esempio Traduttore prende le frasi che vengono inserite e le traduce in latino.

L'esempio Traduttore è assemblato in un ear, e contiene EJB. Di conseguenza, la sua struttura è più complessa di quella dell'esempio Indovina Numero.

Innanzitutto, diamo un'occhiata all'aggregatore ear, che è situato nel modulo webbeans-translator-ear. Maven genera automaticamente il file application.xml:


<plugin>
   <groupId
>org.apache.maven.plugins</groupId>
   <artifactId
>maven-ear-plugin</artifactId>
   <configuration>
      <modules>
         <webModule>
            <groupId
>org.jboss.webbeans.examples.translator</groupId>
            <artifactId
>webbeans-translator-war</artifactId>
            <contextRoot
>/webbeans-translator</contextRoot>
         </webModule>
      </modules>
   </configuration>
</plugin
>

Qua viene impostato il context path, in modo da avere un url gradevole (http://localhost:8080/webbeans-translator).

Suggerimento

Se non si sta usando Maven per generare questi file, sarebbe necessario avere il file META-INF/application.xml:


<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd"
             version="5">
  <display-name
>webbeans-translator-ear</display-name>
  <description
>Ear Example for the reference implementation of JSR 299: Web Beans</description>
  
  <module>
    <web>
      <web-uri
>webbeans-translator.war</web-uri>
      <context-root
>/webbeans-translator</context-root>
    </web>
  </module>
  <module>
    <ejb
>webbeans-translator.jar</ejb>
  </module>
</application
>

Next, lets look at the war. Just as in the numberguess example, we have a faces-config.xml (to enable Facelets) and a web.xml (to enable JSF) in WebContent/WEB-INF.

Più interessante è il facelet usato per tradurre il testo. Proprio come nell'esempio Indovina Numero c'è un template, che circoscrive la form (qui omessa per brevità):


<h:form id="NumberGuessMain">
            
   <table>
      <tr align="center" style="font-weight: bold" >
         <td>
            Your text
         </td>
         <td>
            Translation
         </td>
      </tr>
      <tr>
         <td>
            <h:inputTextarea id="text" value="#{translator.text}" required="true" rows="5" cols="80" />
         </td>
         <td>
            <h:outputText value="#{translator.translatedText}" />
         </td>
      </tr>
   </table>
   <div>
      <h:commandButton id="button" value="Translate" action="#{translator.translate}"/>
   </div>
   
</h:form
>

L'utente può inserire del testo nell'area di testo sulla sinistra e premere il pulsante di traduzione per vedere il risultato sulla destra.

Infine, si esamini il modulo ejb, webbeans-translator-ejb. In src/main/resources/META-INF si trova un file vuoto web-beans.xml, usato per marcare l'archivio come contenente Web Beans.

Abbiamo lasciato per ultimo il boccone più prelibato, il codice! Il progetto ha due bean semplici, SentenceParser e TextTranslator e due bean enterprise, TranslatorControllerBean e SentenceTranslator. Dovreste ormai essere piuttosto familiari all'aspetto di un Web Bean, così ci limiteremo a evidenziare le parti più interessanti.

Sia SentenceParser che TextTranslator sono bean dependenti, e TextTranslator usa l'inizializzazione via costruttore:

public class TextTranslator { 

   private SentenceParser sentenceParser; 
   private Translator sentenceTranslator; 
   
   @Initializer
   TextTranslator(SentenceParser sentenceParser, Translator sentenceTranslator) 
   { 
      this.sentenceParser = sentenceParser; 
      this.sentenceTranslator = sentenceTranslator;

TextTranslator è un bean stateless (con un'interfaccia business locale), dove avviene la magia - naturalmente, non potevamo sviluppare un traduttore completo, ma gli abbiamo dato un buon avvio!

Infine, vi è un controller orientato all'UI, che raccoglie il testo dall'utente, e lo invia al traduttore. Questo è un bean stateful di sessione, dotato di nome, con scope richiesta, in cui viene iniettato il traduttore.

@Stateful

@RequestScoped
@Named("translator")
public class TranslatorControllerBean implements TranslatorController
{
   
   @Current TextTranslator translator;

Il bean possiede pure dei metodi getter e setter per tutti i campi della pagina.

Poichè si tratta di un bean stateful di sessione, è necessario un metodo di rimozione (remove method):

   @Remove

   public void remove()
   {
      
   }

Il manager Web Beans chiamerà il metodo di rimozione quando il bean verrà distrutto; in questo caso al termine della richiesta.

That concludes our short tour of the Web Beans examples. For more on Web Beans , or to help out, please visit http://www.seamframework.org/WebBeans/Development.

Abbiamo bisogno di aiuto in tutte le aree - soluzione dei bug, scrittura di nuove caratteristiche ed esempi e traduzione di questa guida.

Web Beans supporta tre meccanismi primari per la dependency injection:

Iniezione dei parametri del costruttore

public class Checkout {

        
    private final ShoppingCart cart;
    
    @Initializer
    public Checkout(ShoppingCart cart) {
        this.cart = cart;
    }
}

Iniezione dei parametri del metodo inizializzatore (initializer method):

public class Checkout {

        
    private ShoppingCart cart;
    @Initializer 
    void setShoppingCart(ShoppingCart cart) {
        this.cart = cart;
    }
    
}

Iniezione diretta degli attributi

public class Checkout {


    private @Current ShoppingCart cart;
    
}

La dependency injection avviene sempre quando il Web Bean viene istanziato la prima volta.

  • Innanzitutto, il Web Bean manager chiama il costruttore, per ottenere un'istanza del Web Bean.

  • Quindi, il Web Bean manager inizializza i valori di tutti i campi del Web Bean soggetti ad iniezione.

  • Quindi, il Web Bean manager chiama tutti i metodi inizializzatori del Web Bean.

  • Infine, se ne esiste uno, viene chiamato il metodo del Web Bean annotato con @PostConstruct.

L'iniezione dei parametri del costruttore non è supportata per gli EJB, poiché gli EJB sono istanziati dal container EJB, non dal manager Web Bean.

I parametri dei costruttori e dei metodi di inizializzazione non devono essere annotati esplicitamente quando il tipo del binding è @Current, quello predefinito. I campi iniettati, comunque, devono specificare il tipo del binding, anche quando il tipo del binding è quello predefinito. Se il campo non specifica il tipo del binding, non verrà iniettato.

I metodi produttori supportano anche l'iniezione dei parametri:

@Produces Checkout createCheckout(ShoppingCart cart) {

    return new Checkout(cart);
}

Infine, i metodi observer (che vedremo in Capitolo 9, Eventi), i metodi disposal e i metodi distruttori supportano tutti l'iniezione dei parametri.

Le specifiche Web Beans definiscono una procedura, chiamata typesafe resolution algorithm (algoritmo di risoluzione sicura rispetto ai tipi), che il manager Web Bean segue quando deve identificare il Web Beanda iniettare in punto di iniezione. Questo algoritmo di primo acchito sembra complesso, ma una volta che lo si è compreso, in realtà, risulta piuttosto intuitivo. La risoluzione sicura dei tipi viene eseguita durante l'inizializzazione del sistema (system initialization time), il che significa che il manager Web Bean informerà immediatamente un utente se le dipendenze di un Web Bean non possono essere soddisfatte, lanciando una UnsatisfiedDependencyException o una AmbiguousDependencyException.

Lo scopo di questo algoritmo è di permettere a più Web Bean di implementare la stessa tipo definito dall'API e:

  • permettere al client di selezionare l'implementazione richiesta usando le binding annotations,

  • permettere all'installatore dell'applicazione (deployer) di selezionare quale implementazione è appropriata per un particolare deploy, senza cambiamenti al client, abilitando o disabilitando i tipi di deploy, o

  • permette ad un'implementazione della API di fare l'override di un'altra implementazione della stessa API a deployment time, senza apportare modifiche al client, usando la precedenza fra tipi di deploy (deployment type precedence).

Indaghiamo come il manager di Web Beans individua un Web Bean da iniettare.

Se esiste più di un Web Bean che implementa un particolare tipo di API, il punto di iniezione può specificare esattamente quale Web Bean dovrebbe essere iniettato usando una binding annotation. Per esempio, ci potrebbero essere due implementazioni di PaymentProcessor:

@PayByCheque

public class ChequePaymentProcessor implements PaymentProcessor {
    public void process(Payment payment) { ... }
}
@PayByCreditCard

public class CreditCardPaymentProcessor implements PaymentProcessor {
    public void process(Payment payment) { ... }
}

Dove @PayByCheque e @PayByCreditCard sono binding annotation:

@Retention(RUNTIME)

@Target({TYPE, METHOD, FIELD, PARAMETER})
@BindingType
public @interface PayByCheque {}
@Retention(RUNTIME)

@Target({TYPE, METHOD, FIELD, PARAMETER})
@BindingType
public @interface PayByCreditCard {}

Lo sviluppatore di un Web Bean client usa la binding annotation per specificare esattamente quale Web Bean debba essere iniettato.

Utilizzando l'iniezione a livello di campo:

@PayByCheque PaymentProcessor chequePaymentProcessor;

@PayByCreditCard PaymentProcessor creditCardPaymentProcessor;

Utilizzando l'iniezione a livello di metodo inizializzatore:

@Initializer

public void setPaymentProcessors(@PayByCheque PaymentProcessor chequePaymentProcessor, 
                                 @PayByCreditCard PaymentProcessor creditCardPaymentProcessor) {
   this.chequePaymentProcessor = chequePaymentProcessor;
   this.creditCardPaymentProcessor = creditCardPaymentProcessor;
}

O usando l'iniezione a livello di costruttore:

@Initializer

public Checkout(@PayByCheque PaymentProcessor chequePaymentProcessor, 
                @PayByCreditCard PaymentProcessor creditCardPaymentProcessor) {
   this.chequePaymentProcessor = chequePaymentProcessor;
   this.creditCardPaymentProcessor = creditCardPaymentProcessor;
}

Tutti i Web Bean hanno un tipo di deployment (deployment type). Ogni tipo di deployment identifica un insieme di Web Bean che dovrebbe essere installato in modo condizionale in corrispondenza ad alcuni deploy del sistema.

Per esempio, potremmo definire un tipo di deploy denominato @Mock, che identifichi i Web Bean da installare soltanto quando il sistema è posto in esecuzione in un ambiente di test integrato:

@Retention(RUNTIME)

  @Target({TYPE, METHOD})
  @DeploymentType
  public @interface Mock {}

Supponiamo di avere alcuni Web Bean che interagiscano con un sistema di pagamenti esterno:

public class ExternalPaymentProcessor {

        
    public void process(Payment p) {
        ...
    }
    
}

Dal momento che questo Web Bean non specifica esplicitamente un tipo di deploy, ha il tipo di deploy predefinito @Production.

Per le attività di test (d'unità o integrazione), il sistema esterno è lento o non disponibile. Così sarebbe necessario creare un oggetto mock:

@Mock 

public class MockPaymentProcessor implements PaymentProcessor {
    @Override
    public void process(Payment p) {
        p.setSuccessful(true);
    }
}

Ma in che modo il manager Web Bean determina quale implementazione usare con un particolare deploy?

Se avete prestato attenzione, vi state probabilmente chiedendo come il manager Web Bean decida quale implementazione scegliere — ExternalPaymentProcessor o MockPaymentProcessor — Si consideri cosa succede quando il manager incontra questo punto di iniezione:

@Current PaymentProcessor paymentProcessor

Vi sono ora due Web Bean che soddisfano l'interfaccia di PaymentProcessor. Naturalmente, non è possibile utilizzare una binding annotation per eliminare l'ambiguità, poiché le binding annotation sono cablate nel sorgente in corrispondenza al punto di iniezione, e noi vogliamo che il manager sia in grado di decidere a deployment time!

La soluzione a questo problema sta nel fatto che ciascun tipo di deploy ha una diversa precedenza. La precedenza dei tipi di deploy è determinata dall'ordine con cui appaiono in web-beans.xml. Nel nostro esempio, @Mock compare dopo @Production cosicché ha una precedenza più alta.

Ogni volta che il manager scopre che più di un Web Bean potrebbe soddisfare il contratto (interfaccia più binding annotation) specificato da un punto di iniezione, passa a considerare la precedenza relativa dei Web Bean. Se uno ha una precedenza superiore a quella degli altri, questo viene scelto per essere iniettato. Così, nel nostro esempio, il manager Web Bean inietterà MockPaymentProcessor quando viene eseguito nel nostro ambiente di test (che è esattamente ciò che vogliamo).

E' interessante confrontare questa funzionalità con le architetture di gestone oggi in voga. Vari container "lightweight" permettono il deploy condizionale di classi che esistono nel classpath, ma le classi che devono essere installate devono essere elencate esplicitamente ed individualmente nel codice di configurazione o in qualche file XML di configurazione. Web Beans supporta certo la definizione e configurazione dei Web Bean attraverso l'XML, ma nei casi comuni in cui non si renda necessaria una configurazione complicata, i tipi di deploy permettono di abilitare un insieme completo di Web Bean con una sola linea di XML. Al contempo, uno sviluppatore che esamini il codice, potrà facilmente identificare gli scenari di deploy in cui il Web Bean sarà utilizzato.

I client di un Web Bean che sono stati iniettati solitamente non hanno un riferimento diretto all'istanza del Web Bean.

Immaginiamo che un Web Bean associato allo scope applicazione tenga un riferimento diretto a un Web Bean associato allo scope richiesta. Il Web Bean con scope applicazione è condiviso fra molte diverse richieste. Comunque, ciascuna richiesta dovrebbe vedere una diversa istanza del Web bean con scope richiesta!

Immaginiamo ora che un Web Bean con scope sessione abbia un riferimento diretto a un Web Bean con scope applicazione . Ogni tanto, il contesto della sessione viene serializzato su disco in modo da usare la memoria in modo più efficiente. Comunque, l'istanza del Web Bean con scope applicazione non dovrebbe essere serializzato insieme al Web Bean con scope sessione!

Quindi, a meno che un Web Bean abbia lo scope predefinito @Dependent, il manager Web Bean deve rendere indiretti tutti i riferimenti al Web Bean iniettati attraverso un oggetto proxy. Questo client proxy ha la responsabilità di assicurare che l'istanza del Web Bean su cui viene invocato un metodo sia l'istanza associata al contesto corrente. Il client proxy, inoltre, permette ai Web Bean associati a contesti come quello di sessione di essere salvati su disco senza serializzare ricorsivamente altri Web Beans che siano stati iniettati.

Purtroppo, a causa di limitazioni del linguaggio Java, alcuni tipi Java non possono essere gestiti tramite un proxy dal manager Web Bean. Quindi, il manager Web Bean lancia un'eccezione UnproxyableDependencyException se il tipo di un punto di iniezione non può essere gestito tramite proxy.

I seguenti tipi Java non possono essere gestiti tramite proxy dal manager Web Bean:

Di solito è molto facile rimediare a una UnproxyableDependencyException. Si deve semplicimente aggiungere un costruttore privo di parametri alla classe iniettata, introdurre un'interfaccia, o modificare lo scope del Web Bean iniettato a @Dependent.

Certi tipi di oggetti dipendenti — Web Bean con scope @Dependent — hanno bisogno di avere informazioni riguardo l'oggetto o il punto in cui sono iniettati per fare quello che devono. Per esempio:

Un Web Bean con scope @Dependent può essere iniettato con un'istanza di InjectionPoint e accedere i metadati riguardanti il punto di iniezione cui appartiene.

Vediamo un esempio. Il codice seguente è prolisso e vulnerabile a problemi di refactoring:

Logger log = Logger.getLogger(MyClass.class.getName());

Questo piccolo e intelligente metodo produttore permette di iniettare un Logger JDK senza specificare esplicitamente la categoria di log:

class LogFactory {


   @Produces Logger createLogger(InjectionPoint injectionPoint) { 
      return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName()); 
   }
}

Ora è possibile scrivere:

@Current Logger log;

Non siete convinti? Eccovi un secondo esempio. Per iniettare parametri HTTP, è necessario definire un tipo di binding:

@BindingType

@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface HttpParam {
   @NonBinding public String value();
}

Potremmo usare questo tipo di binding in corrispondenza ai punti di iniezione in questo modo:

@HttpParam("username") String username;

@HttpParam("password") String password;

Il seguente metodo produttore esegue il lavoro:

class HttpParams


   @Produces @HttpParam("")
   String getParamValue(ServletRequest request, InjectionPoint ip) {
      return request.getParameter(ip.getAnnotation(HttpParam.class).value());
   }
}

(Occorre notare che il membro value() dell'annotazione HttpParam viene ignorato dal manager Web Bean poiché è annotato con @NonBinding.)

Il manager Web Bean fornisce un Web Bean di sistema che implementa l'interfaccia InjectionPoint:

public interface InjectionPoint { 

   public Object getInstance(); 
   public Bean<?> getBean(); 
   public Member getMember(): 
   public <extends Annotation
> T getAnnotation(Class<T
> annotation); 
   public Set<extends Annotation
> getAnnotations(); 
}

Finora si sono visti pochi esempi di annotazioni di tipi di scope. Gli scope di un Web Bean determinano il ciclo di vita del Web Bean. Lo scope determina anche quali client fanno riferimento a quali istanze di Web Bean. Secondo la specifica Web Bean, uno scope determina:

  • Quando una nuova istanza di un Web Bean con tale scope viene creata

  • Quando un'istanza esistente di un Web Bean con tale scope viene distrutta

  • Quali riferimenti iniettati puntano a istanze di un Web Bean con tale scope

Per esempio, se esiste un Web Bean con scope di sessione, CurrentUser, tutti i Web Bean che vengono chiamati nel contesto della medesima HttpSession vedranno la stessa istanza di CurrentUser. Quest'istanza verrà automaticamente creata la prima volta che in tale sessione occorre CurrentUser, e verrà distrutta automaticamente quando la sessione termina.

Lo scope di conversazione di Web Beans è un pò come il tradizionale scope di sessione in cui viene mantenuto lo stato associato all'utente del sistema, e vengono create richiest multiple al server. Comunque, a differenza dello scope di sessione, lo scope di conversazione:

Una conversazione rappresenta un task, un'unità di lavoro dal punto di vista dell'utente. Il contesto di conversazione mantiene uno stato associato all'utente che sta lavorando. Se l'utente sta facendo più cose contemporaneamente ci saranno più conversazioni.

Il contesto di conversazione è attivo durante ogni richiesta JSF. Comunque, la maggior parte delle conversazioni vengono distrutte alla fine della richiesta. Se una conversazione deve mantenere lo stato nel corso richieste multiple, deve esplicitamente essere promossa a conversazione long-running.

Web Beans fornisce un Web Bean predefinito per controllare il ciclo di vita delle conversazioni in un'applicazione JSF. QUesto Web Bean può essere ottenuto per iniezione:

@Current Conversation conversation;

Per promuovere a long-running la conversazione associata alla richiesta corrente, occorre chiamare il metodo begin() dal codice dell'applicazione. Per schedulare la distruzione del contesto attuale della conversazione long-running, si chiami end().

Nel seguente esempio un Web Bean con scope di conversazione controlla la conversazione alla quale è associato:

@ConversationScoped @Stateful

public class OrderBuilder {
    private Order order;
    private @Current Conversation conversation;
    private @PersistenceContext(type=EXTENDED) EntityManager em;
    
    @Produces public Order getOrder() {
        return order;
    }
    public Order createOrder() {
        order = new Order();
        conversation.begin();
        return order;
    }
    
    public void addLineItem(Product product, int quantity) {
        order.add( new LineItem(product, quantity) );
    }
    public void saveOrder(Order order) {
        em.persist(order);
        conversation.end();
    }
    
    @Remove
    public void destroy() {}
    
}

Questo Web Bean è capace di controlla il proprio ciclo di vita attraverso l'uso della API Conversation. Ma altri Web BEan hanno un ciclo di vita che dipende completamente da un altro oggetto.

In aggiunta ai quattro scope predefiniti, Web Bean fornisce il cosiddetto pseudo-scope dipendente. Questo è lo scope di default per un Web Bean che non dichiara esplicitamente un tipo di scope.

Per esempio questo Web Bean ha uno scope di tipo @Dependent:

public class Calculator { ... }

Quando un punto di iniezione di un Web Bean risolve verso un Web Bean dipendente, viene creata una nuova istanza di Web Bean dipendente ogni volta che il primo Web Bean viene istanziato. Le istanze dei Web Beans dipendenti non vengono mai condivise tra Web Bean differenti o punti di iniezione differenti. Sono oggetti dipendenti di altre istanze Web Bean.

Istanze Web Bean dipendenti vengono distrutte quando viene distrutta l'istanza da cui dipendono.

Web Beans facilità l'ottenimento di un'istanza dipendente di una classe Java o bean EJB, anche se la classe o bean EJB sono già dichiarati come Web Bean con qualche altro tipo di scope.

I metodi produttori consentono di superare alcune limitazioni che sorgono quando il manager Web Bean è responsabile dell'istanziamento degli oggetti al posto dell'applicazione. Questi metodi sono anche il modo migliore per integrare gli oggetti che non sono Web Beans dentro l'ambiente Web Beans. (Si incontrerà un secondo approccio in Capitolo 12, Definire i Web Beans tramite XML.)

Secondo la specifica:

Un metodo produttore Web Bean agisce come sorgente di oggetti da iniettare, dove:

  • gli oggetti da iniettare non è richiesto siano istanze di Web Beans,

  • il tipo concreto di oggetti da iniettare può variare a runtime o

  • gli oggetti richiedono alcune inizializzazini personalizzate che non vengono eseguite dal costruttore Web Bean

Per esempio un metodo produttore consente di:

  • esporre un entity JPA comeWeb Bean,

  • esporre qualsiasi classe JDK comeWeb Bean,

  • definire Web Beans multipli, con scope o inizializzazione differenti, per la stessa implementazionedi classe, o

  • variare l'implementazione di un tipo di API a runtime

In particolare, i metodi produttori consentono di usare un polimorfismo a runtime con i Web Beans. Come visto, i tipi di deploy sono soluzioni al problema del polimorfismo durante la fase di deploy. Ma una volta che il sistema viene deployato, l'implementazione del Web Bean viene fissata. Un metodo produttore non ha questi limiti:

@SessionScoped

public class Preferences {
    
    private PaymentStrategyType paymentStrategy;
    
    ...
    
    @Produces @Preferred 
    public PaymentStrategy getPaymentStrategy() {
        switch (paymentStrategy) {
            case CREDIT_CARD: return new CreditCardPaymentStrategy();
            case CHEQUE: return new ChequePaymentStrategy();
            case PAYPAL: return new PayPalPaymentStrategy();
            default: return null;
        } 
    }
    
}

Si consideri un punto di iniezione:

@Preferred PaymentStrategy paymentStrat;

Questo punto di iniezione ha lo stesso tipo e annotazioni di binding del metodo produttore, e quindi risolve i metodi produttori usando le regole di iniezione dei Web Beans. Il metodo produttore verrà chiamato dal manager Web Bean per ottenere un'istanza per servire questo punto di iniezione.

C'è un potenziale problema con il codice visto sopra. Le implementazioni di CreditCardPaymentStrategy vengono istanziate usando l'operatore Java new. Gli oggetti istanziati direttamente dall'applicazione non possono sfruttare la dependency injection e non hanno interceptor.

Se questo non è ciò che si vuole, è possibile usare la dependency injection nel metodo produttore per ottenere istanze Web Bean:

@Produces @Preferred @SessionScoped

public PaymentStrategy getPaymentStrategy(CreditCardPaymentStrategy ccps,
                                          ChequePaymentStrategy cps,
                                          PayPalPaymentStrategy ppps) {
    switch (paymentStrategy) {
        case CREDIT_CARD: return ccps;
        case CHEQUE: return cps;
        case PAYPAL: return ppps;
        default: return null;
    } 
}

Ma cosa succede se CreditCardPaymentStrategy è un Web Bean con scope di tipo richiesta? Il metodo produttore ha l'effetto di "promuovere" l'istanza corrente con scope di tipo richiesta a scope di tipo sessione. Questo è quasi certamente un bug! L'oggetto con scope richiesta verrà distrutto dal manager Web Bean prima che la sessione termini. Quest'errore non verrà rilevato dal manager Web Bean, quindi si faccia attenzione quando si restituiscono istanze Web Bean dai metodi produttori!

Ci sono almeno 3 modi per correggere questo bug. Si può cambiare lo scope dell'implementazione di CreditCardPaymentStrategy, ma questo non influenzerebbe gli altri client di questo Web Bean. Un'opzione migliore sarebbe quella di cambiare lo scope del metodo produttore a @Dependent o @RequestScoped.

Ma una soluzione più comune è quella di usare la speciale annotazione di binding @New.

Il primo tema saliente di Web Beans è l'accoppiamento debole (loose coupling). Abbiamo già visto tre modi per realizzarlo:

Queste tecniche servono a realizzare l'accoppiamento debole (loose coupling) di client e server. Il client non è più strettamente legato all'implementazione di una API, né è tenuto a gestire il ciclo di vita dell'oggetto server. Questo approccio permette agli oggetti stateful di interagire come se fossero servizi.

L'accoppiamento debole (loose coupling) rende un sistema più dinamico. Il sistema può rispondere ai cambiamenti in un modo ben definito. In passato, i framework che hanno cercato di fornire le funzionalità e gli strumenti sopraelencati, l'hanno puntualmente fatto a discapito della sicurezza dei tipi (type safety). Web Beans è la prima tecnologia a raggiungere questo livello di puntualmente accoppiamento debole (loose coupling) in modo sicuro rispetto all'uso dei tipi.

Web Beans fornisce tre strumenti extra importanti che ampliano l'obiettivo del loose coupling:

Innanzitutto esploriamo gli interceptor.

Web Beans riutilizza l'architettura base degli interceptor di EJB3.0, estendendo la funzionalità in due direzioni:

  • Qualsiasi Web Bean può avere interceptor, non solo i session bean.

  • Web Bean fornisce un più sofisticato approccio basato su annotazioni per associare interceptor ai Web Beans.

"La specifica Web Bean definisce due tipi di punti di intercettazione:

  • intercettazione del metodo di business, e

  • intercettazione della chiamata del ciclo di vita

Un interceptor di un metodo di business si applica alle invocazioni di metodi del Web Bean da parte di client del Web Bean:

public class TransactionInterceptor {

    @AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}

Un interceptor di chiamata del ciclo di vita si applica alle invocazioni delle chiamate del ciclo di vita da parte del container:

public class DependencyInjectionInterceptor {

    @PostConstruct public void injectDependencies(InvocationContext ctx) { ... }
}

Una classe interceptor può intercettare entrambi le chiamate del ciclo di vita ed i metodi di business.

Gli interceptor sono un potente modo per catturare e separare i concern (N.d.T. un concern è un particolare concetto o area di interesse) che sono ortogonali al sistema tipo. Qualsiasi interceptor è capace di intercettare le invocazioni di qualsiasi tipo Java. Questo li rende perfetti per risolvere concern tecnici quali gestione delle transazioni e la sicurezza. Comunque, per natura, gli interceptor non sono consapevoli dell'attuale semantica degli eventi che intercettano. Quindi gli interceptor non sono il giusto strumento per separare i concern di tipo business.

Il contrario è vero per i decoratori. Un decoratore intercetta le invocazioni solamente per una certa interfaccia Java, e quindi è consapevole della semantica legata a questa. Ciò rende i decoratori uno strumento perfetto per modellare alcuni tipi di concern di business. E significa pure che un decoratore non ha la generalità di un interceptor. I decoratori non sono capaci di risolvere i concern tecnici che agiscono per diversi tipi.

Supponiamo di avere un'interfaccia che rappresenti degli account:

public interface Account {

    public BigDecimal getBalance();
    public User getOwner();
    public void withdraw(BigDecimal amount);
    public void deposit(BigDecimal amount);
}

Parecchi Web Beans del nostro sistema implementano l'interfaccia Account. Abbiamo come comune requisito legale, per ogni tipo di account, che le transazioni lunghe vengano registrate dal sistema in uno speciale log. Questo è un lavoro perfetto per un decoratore.

Un decorator è un semplice Web Beans che implementa il tipo che decora ed è annotato con @Decorator."

@Decorator

public abstract class LargeTransactionDecorator 
        implements Account {
    
    @Decorates Account account;
    
    @PersistenceContext EntityManager em;
    
    public void withdraw(BigDecimal amount) {
        account.withdraw(amount);
        if ( amount.compareTo(LARGE_AMOUNT)
>0 ) {
            em.persist( new LoggedWithdrawl(amount) );
        }
    }
    
    public void deposit(BigDecimal amount);
        account.deposit(amount);
        if ( amount.compareTo(LARGE_AMOUNT)
>0 ) {
            em.persist( new LoggedDeposit(amount) );
        }
    }
    
}

Diversamente dai semplici Web Beans, un decoratore può essere una classe astratta. Se un decoratore non ha niente da fare per un particolare metodo, allora non occorre implementare quel metodo.

Il sistema di notifica a eventi di Web Beans consente a Web Beans di interagire in maniera totalmente disaccoppiata. I produttori di eventi sollevano eventi che vengono consegnati agli osservatori di eventi tramite il manager Web Bean. Lo schema base può suonare simile al familiare pattern observer/observable, ma ci sono un paio di differenze:

  • non solo i produttori di eventi sono disaccoppiati dagli osservatori; gli osservatori sono completamente disaccoppiati dai produttori,

  • gli osservatori possono specificare una combinazione di "selettori" per restringere il set di notifiche di eventi da ricevere, e

  • gli osservatori possono essere notificati immediatamente, o possono specificare che la consegna degli eventi venga ritardata fino alla fine della transazione conrrente

Il produttore dell'evento può ottenere tramite iniezione un oggetto notificatore d'evento:

@Observable Event<Document

> documentEvent

L'annotazione @Observable definisce implicitamente un Web Bean con scope @Dependent e tipo di deploy @Standard, con un'implementazione fornita dal manager Web Bean.

Un produttore solleva eventi chiamando il metodo fire() dell'intefaccia Event, passando un oggetto evento:

documentEvent.fire(document);

Un oggetto evento può essere un'istanza di una classe Java che non ha variabili tipo o parametri tipo wildcard. L'evento verrà consegnato ad ogni metodo osservatore che:

Il manager Web Bean chiama semplicemente tutti i metodi osservatori, passando l'oggento evento come valore del parametro evento. Se il metodo osservatore lancia un'eccezione, il manager Web Bean smette di chiamare i metodi osservatori, e l'eccezione viene rilanciata dal metodo fire().

Per specificare un "selettore" il produttore d'evento può passare un'istanza del tipo di binding d'evento al metodo fire():

documentEvent.fire( document, new AnnotationLiteral<Updated

>(){} );

La classe helper AnnotationLiteral rende possibile istanziare inline i tipi di binding, dato che questo risulta difficile da fare in Java.

L'evento verrà consegnato ad ogni metodo osservatore che:

In alternativa gli event binding possono essere specificati annotando il punto di iniezione del notificato d'evento:

@Observable @Updated Event<Document

> documentUpdatedEvent

Quindi ciascun evento sollevato tramite quest'istanza di Event ha annotato l'event binding. L'evento verrà consegnato ad ogni metodo osservatore che:

Gli osservatori transazionali ricevono notifiche d'evento prima o dopo la fase di completamento della transazione, nella quale l'evento viene sollevato. Per esempio, il seguente metodo osservatore ha bisogno di aggiornare il set di risultati della query memorizzato nel contesto dell'applicazione, ma solo quando hanno successo le transazioni che aggiornano l'albero Category.

public void refreshCategoryTree(@AfterTransactionSuccess @Observes CategoryUpdateEvent event) { ... }

Ci sono tre tipi di osservatori transazionali:

Gli osservatori transazionali sono molto importanti in un modello ad oggetto stateful come Web Beans, poiché lo stato è spesso mantenuto per un tempo più lungo di una singola transazione atomica.

Si immagini di avere cachato un risultato di query JPA nello scope di applicazione:

@ApplicationScoped @Singleton

public class Catalog {
    @PersistenceContext EntityManager em;
    
    List<Product
> products;
    @Produces @Catalog 
    List<Product
> getCatalog() {
        if (products==null) {
            products = em.createQuery("select p from Product p where p.deleted = false")
                .getResultList();
        }
        return products;
    }
    
}

Di tanto in tanto un Product viene creato o cancellato. Quando questo avviene occorre aggiornare il catalogo del Product. Ma si dovrebbe aspettare che la transazione abbia completato con successo prima di eseguire l'aggiornamento!

Il Web Bean che crea o cancella Product può sollevare eventi, per esempio:

@Stateless

public class ProductManager {
    @PersistenceContext EntityManager em;
    @Observable Event<Product
> productEvent;
    public void delete(Product product) {
        em.delete(product);
        productEvent.fire(product, new AnnotationLiteral<Deleted
>(){});
    }
    
    public void persist(Product product) {
        em.persist(product);
        productEvent.fire(product, new AnnotationLiteral<Created
>(){});
    }
    
    ...
    
}

E ora Catalog può osservare gli eventi dopo il completamento (con successo) della transazione:

@ApplicationScoped @Singleton

public class Catalog {
    ...
    
    void addProduct(@AfterTransactionSuccess @Observes @Created Product product) {
        products.add(product);
    }
    
    void addProduct(@AfterTransactionSuccess @Observes @Deleted Product product) {
        products.remove(product);
    }
    
}

Il secondo tema saliente di Web Beans è lo tipizzazione forte (strong typing). Le informazioni riguardanti dipendenze, interceptor e decorator di un Web Bean, e le informazioni sui i consumatori relativi ad un produttore di eventi, sono contenute in costrutti Java sicuri rispetto ai tipi (typesafe) che possono essere validati dal compilatore.

Non si vedono identificatori di tipo stringa nel codice basato su Web Beans, non perché il framework li nasconde usando regole intelligenti nell'assegnamento dei valori di default — la cosiddetta "configurazione per convenzione (configuration by convention)" — ma semplicemente perché non ci sono stringhe, tanto per cominciare!

L'ovvio beneficio di questo approccio è che qualunque IDE può fornire autocompletamento, validazione e refactoring senza che sia necessario realizzare dei tool appositi. Ma c'è un secondo beneficio meno immediatamente ovvio. Si scopre che quando si incomincia a pensare di identificare oggetti, eventi o interceptor usando annotazioni invece di nomi, si ha l'opportunità di elevare il livello semantico del proprio codice.

Web Beans incoraggia a sviluppare annotazioni che modellano concetti, per esempio,

invece di usare nomi composti come

Le annotazioni sono riutilizzabili. Aiutano a descrivere caratteristiche comuni di parti diverse del sistema. Ci aiutano a categorizzare e comprendere il nostro stesso codice. Ci aiutano ad affrontare i concern comuni in un modo comune. Rendono il nostro codice più elegante e comprensibile.

Gli stereotipi (stereotypes) di Web Beans fanno fare un ulteriore passo in avanti a questa idea. Uno stereotipo descrive un ruolo comune nell'architettura di un'applicazione. Incapsula in un unico pacchetto riutilizzabile varie proprietà del ruolo stesso, inclusi lo scope, gli interceptor bindings, il tipo di deployment, etc, .

Persino i metadati XML di Web Beans sono fortemente tipizzati (strongly typed)! Non esistendo un compilatore XML, Web Beans si basa sugli schemi XML per validare i tipi Java e gli attributi che compaiono nell'XML. Questo approccio finisce col rendere il codice XML più informato, proprio come le annotazioni rendono il codice Java + informato.

Ora siamo pronti ad incontrare alcune caratteristiche più avanzate di Web Beans. Tenete a mente che tali caratteristiche esistono sia per rendere il nostro codice più facile da validare che per renderlo più comprensibile. La maggior parte delle volte non è necessario usare tali carattestiche, ma, se usate con accortezza, si arriverà ad apprezzerne l'efficacia.

Secondo la specifica Web Beans:

In molti sistemi l'uso di pattern architetturali produce un set di ruoli Web Bean ricorrenti. Uno stereotipo consente allo sviluppatore di framework di identificare tale ruolo e di dichiarare alcuni metadati comuni per i Web Bean con tale ruolo come parte principale.

Uno stereotipo incapsula qualsiasi combinazione di:

  • un tipo di deploy di default,

  • un tipo di scope di default,

  • una restrizione sullo scope del Web Bean,

  • un requisito che il Web Bean implementi o estenda un certo tipo, e

  • un set di annotazioni di interceptor binding.

Uno stereotipo può anche specificare che tutti i Web Beans con tale stereotipo abbiano nomi Web Bean di default.

Un Web Bean può dichiarare zero, uno o più stereotipi.

Uno stereotipo è un tipo di annotazione Java. Questo stereotipo identifica le classi di azione in alcuni framework MVC:

@Retention(RUNTIME)

@Target(TYPE)
@Stereotype
public @interface Action {}

Lo stereotipo viene impiegato applicando l'annotazione al Web Bean.

@Action 

public class LoginAction { ... }

Si è già visto come il modello di dependency injection di Web Beans consenta l'override dell'implementazione di un API a deployment time. Per esempio, il seguente Web Bean enterprise fornisce un'implementazione di un API PaymentProcessor in produzione:

@CreditCard @Stateless

public class CreditCardPaymentProcessor 
        implements PaymentProcessor {
    ...
}

Ma in quest'ambiente di prova si procede con l'override dell'implementazione di PaymentProcessor con un Web Bean differente:

@CreditCard @Stateless @Staging

public class StagingCreditCardPaymentProcessor 
        implements PaymentProcessor {
    ...
}

Quello che si è cercato di fare con StagingCreditCardPaymentProcessor è sostituire completamente AsyncPaymentProcessor in un particolare deployment del sistema. In tale deployment, il tipo di deploy @Staging avrebbe più alta priorità del tipo di deploy di default @Production, e quindi client con il seguente punto di iniezione:

@CreditCard PaymentProcessor ccpp

riceverebbero un'istanza di StagingCreditCardPaymentProcessor.

Sfortunatamente ci sono parecchie trappole in cui è facile cadere:

  • il Web Bean con più alta priorità potrebbe non implementare tutti i tipi di API del Web Bean di cui tenta di fare override,

  • il Web Bean con più alta priorità potrebbe non dichiare tutti i tipi di binding del Web Bean di cui tenta di fare override,

  • il Web Bean con più alta priorità potrebbe non avere lo stesso nome del Web Bean di cui tenta di fare override, oppure

  • il Web Bean di cui tenta di fare override potrebbe dichiarare un metodo produttore, metodo distruttore o metodo osservatore.

In ciascuno di questi casi, il Web Bean di cui si tenta l'override potrebbe ancora venire chiamato a runtime. Quindi, l'override è qualcosa di incline all'errore per lo sviluppatore.

Web Beans fornisce una funzionalità speciale chiamata specializzazione, che aiuta lo sviluppatore ad evitare queste trappole. La specializzazione sembra inizialmente un pò esoterica, ma in pratica è facile da usare, e si apprezzerà veramente la sicurezza extra che fornisce.

Finora si sono visti molti esempi di Web Bean dichiarati usando annotazioni. Comunque ci sono varie occasioni in cui non è possibile usare le annotazioni per definire un Web Bean:

  • quando la classe d'implementazione proviene da qualche libreria preesistente, o

  • quando devrebbero esserci Web Beans multipli con la stessa classe d'implementazione.

In entrambi i casi Web Beans fornisce due opzioni:

  • scrivere un metodo produttore, o

  • dichiarare il Web Bean usando XML.

Molti framework utilizzano XML per scrivere metadati relazionati alle classi. Web Beans usa un approccio molto diverso rispetto agli altri framework per specificare i nomi delle classi Java, dei campi o dei metodi. Invece di scrivere i nomi delle classi o dei membri come valori stringa di elementi e atttributi XML, Web Beans consente di utilizzare il nome della classe o del membro come nome dell'elemento XML.

Il vantaggio di quest'approccio è che risulta possibile scrivere uno schemaXML che previene gli errori di scrittura nei documenti XML. E' comunque possibile per un tool generare lo schema XML in modo automatico dal codice Java compilato. Oppure un ambiente di sviluppo integrato può eseguire la stessa validazionesenza il bisogno di un passo di generazione intermedio ed esplicito.

Il terzo tema di Web Beans è l'integrazione. Web Beans è stata progettata per funzionare in armonia con altre tecnologie, per aiutare lo sviluppatore a far funzionare assieme le altre tecnologie. Web Beans è una tecnologia aperta. Costituisce una parte dell'ecosistema Java EE, ed è essa stessa la fondazione di un nuovo ecosistema di estensioni portabili e di integrazioni con framework e tecnologie esistenti.

Abbiamo già visto come Web Beans aiuti ad integrare EJB con JSF, permettendo agli EJB di essere legati direttamente alle pagine JSF. Questo non è che l'inizio. Web Beans offre le stesse potenzialità ad altre e diverse tecnologie, quali i motori di gestione di processi di business, altri Framework Web, e modelli a componenti di terze parti. La piattaforma Java EE non potrà mai standardizzare tutte le interessanti tecnologie utilizzate nel mondo dello sviluppo di applicazioni Java, ma Web Beans rende più facile usare in modo trasparente all'interno dell'ambiente Java EE tecnologie che non fanno ancora parte di tale ambiente.

Stiamo per scoprire come sfruttare appieno la piattaforma Java EE in applicazioni che usano Web Beans. Scorreremo anche velocemente un insieme di SPI (Service Provider Interface) fornite per permettere la realizzazione di estensioni portabili di Web Beans. Potrebbe non essere mai necessario usare queste SPI direttamente, ma è opportuno sapere che esistono, qualora possano servire. Soprattutto, le si sfrutterà in modo indiretto ogni volta che si utilizzeranno estensioni di terze parti.

Web Beans è pienamente integrata nell'ambiente Java EE. Web Beans ha accesso alle risorse Java EE ed ai contesti di persistenza JPA. I Web Beans possono essere usati in espressioni Unified EL dentro pagine JSF e JSP. Possono anche essere iniettati negli oggetti, come Servlet e Message-Driven Beans, che non sono Web Beans.

La spedizione dei messaggi usando JMS può essere abbastanza complessa, a causa del numero di oggetti differenti da trattare. Per le code si hanno Queue, QueueConnectionFactory, QueueConnection, QueueSession e QueueSender. Per i topic si hanno Topic, TopicConnectionFactory, TopicConnection, TopicSession e TopicPublisher. Ciascuno di questi oggetti ha il proprio ciclo di vita e modello di thread di cui bisogna (pre)occuparsi.

I Web Beans si prendono cura di tutto questo per noi. Tutto ciò che occorre fare è dichiarare la coda od il topic in web-beans.xml, specificando un


<Queue>
    <destination
>java:comp/env/jms/OrderQueue</destination>
    <connectionFactory
>java:comp/env/jms/QueueConnectionFactory</connectionFactory>
    <myapp:OrderProcessor/>    
</Queue
>

<Topic>
    <destination
>java:comp/env/jms/StockPrices</destination>
    <connectionFactory
>java:comp/env/jms/TopicConnectionFactory</connectionFactory>
    <myapp:StockPrices/>    
</Topic
>

Ora è possibile iniettare Queue, QueueConnection, QueueSession o QueueSender per una coda, oppure Topic, TopicConnection, TopicSession o TopicPublisher per un topic.

@OrderProcessor QueueSender orderSender;

@OrderProcessor QueueSession orderSession;
public void sendMessage() {
    MapMessage msg = orderSession.createMapMessage();
    ...
    orderSender.send(msg);
}
@StockPrices TopicPublisher pricePublisher;

@StockPrices TopicSession priceSession;
public void sendMessage(String price) {
    pricePublisher.send( priceSession.createTextMessage(price) );
}

Il ciclo di vita degli oggetti JMS iniettati è interamente controllato dal manager Web Bean.

Web Beans è inteso essere una piattaforma per framework, estensioni e integrazione con altre tecnologie. Quindi Web Beans espone un set di SPI (Service Provider Interface) per l'uso da parte degli sviluppatori di estensioni portabili a Web Beans. Per esempio, i seguentitipi di estensione sono state prese in considerazione dai progettisti di Web Beans:

  • Integrazione con i motori di Gestione dei Processi di Business,

  • integrazione con framework di terze-parti quali Spring, Seam, GWT o Wicket, e

  • nuova tecnologia basata sul modello di programmazione di Web Beans.

Il nervo centrale per l'estensione di Web Beans è l'oggetto Manager.

L'interfaccia Manager consente di registrare ed ottenere programmaticamente interceptor, decoratori, osservatori e contesti di Web Beans.

public interface Manager

{
   public <T
> Set<Bean<T
>
> resolveByType(Class<T
> type, Annotation... bindings);
   public <T
> Set<Bean<T
>
> resolveByType(TypeLiteral<T
> apiType,
         Annotation... bindings);
   public <T
> T getInstanceByType(Class<T
> type, Annotation... bindings);
   public <T
> T getInstanceByType(TypeLiteral<T
> type,
         Annotation... bindings);
   public Set<Bean<?>
> resolveByName(String name);
   public Object getInstanceByName(String name);
   public <T
> T getInstance(Bean<T
> bean);
   public void fireEvent(Object event, Annotation... bindings);
   public Context getContext(Class<? extends Annotation
> scopeType);
   public Manager addContext(Context context);
   public Manager addBean(Bean<?> bean);
   public Manager addInterceptor(Interceptor interceptor);
   public Manager addDecorator(Decorator decorator);
   public <T
> Manager addObserver(Observer<T
> observer, Class<T
> eventType,
         Annotation... bindings);
   public <T
> Manager addObserver(Observer<T
> observer, TypeLiteral<T
> eventType,
         Annotation... bindings);
   public <T
> Manager removeObserver(Observer<T
> observer, Class<T
> eventType,
         Annotation... bindings);
   public <T
> Manager removeObserver(Observer<T
> observer,
         TypeLiteral<T
> eventType, Annotation... bindings);
   public <T
> Set<Observer<T
>
> resolveObservers(T event, Annotation... bindings);
   public List<Interceptor
> resolveInterceptors(InterceptionType type,
         Annotation... interceptorBindings);
   public List<Decorator
> resolveDecorators(Set<Class<?>
> types,
         Annotation... bindings);
}

Possiamo ottenere un'istanza di Manager via iniezione:

@Current Manager manager

Attualmente Web Bean RI funziona solo in JBoss AS 5; l'integrazione di RI in altri ambienti EE (per esempio in un application server come Glassfish), in un servlet container (come Tomcat), o con un'implementazione EJB3.1 Embedded è abbastanza facile. In questo appendice si discuterà brevemente dei passi necessari.

Web Beans SPI è collocato nel modulo webbeans-ri-spi, ed è impacchettato come webbeans-ri-spi.jar. Alcuni SPI sono opzionali se occorre fare override del comportamento di default, altri sono richiesti.

Tutte le interfacce in SPI supportano il pattern decorator e forniscono una classe Forwarding.

Web Beans RI delega al container la rilevazione dei bean EJB3 e quindi risulta non essere necessario eseguire lo scan delle annotazioni EJB3 o fare il parsing di ejb-jar.xml. Per ciascun EJB nell'applicazione dovrebbe essere rilevato un EJBDescriptor:

public interface EjbDiscovery

{
   public static final String PROPERTY_NAME = EjbDiscovery.class.getName();
   
   /**
    * Gets a descriptor for each EJB in the application
    * 
    * @return The bean class to descriptor map 
    */
   public Iterable<EjbDescriptor<?>
> discoverEjbs();
   
}
public interface EjbDescriptor<T

> {
   
   /**
    * Gets the EJB type
    * 
    * @return The EJB Bean class
    */
   public Class<T
> getType();
   /**
    * Gets the local business interfaces of the EJB
    * 
    * @return An iterator over the local business interfaces
    */
   public Iterable<BusinessInterfaceDescriptor<?>
> getLocalBusinessInterfaces();
   
   /**
    * Gets the remote business interfaces of the EJB
    * 
    * @return An iterator over the remote business interfaces
    */
   public Iterable<BusinessInterfaceDescriptor<?>
> getRemoteBusinessInterfaces();
   
   /**
    * Get the remove methods of the EJB
    * 
    * @return An iterator over the remove methods
    */
   public Iterable<Method
> getRemoveMethods();
   /**
    * Indicates if the bean is stateless
    * 
    * @return True if stateless, false otherwise
    */
   public boolean isStateless();
   /**
    * Indicates if the bean is a EJB 3.1 Singleton
    * 
    * @return True if the bean is a singleton, false otherwise
    */
   public boolean isSingleton();
   /**
    * Indicates if the EJB is stateful
    * 
    * @return True if the bean is stateful, false otherwise
    */
   public boolean isStateful();
   /**
    * Indicates if the EJB is and MDB
    * 
    * @return True if the bean is an MDB, false otherwise
    */
   public boolean isMessageDriven();
   /**
    * Gets the EJB name
    * 
    * @return The name
    */
   public String getEjbName();
   
   
}

Il EjbDescriptor è abbastanza auto-esplicatorio e dovrebbe restituire i metadati rilevanti definiti nella specifica EJB. In aggiunta a queste due interfacce, vi è BusinessInterfaceDescriptor a rappresentare un'interfaccia locale di business (che incapsula la classe d'interfaccia ed il nome jndi usato per la ricerca di una istanza EJB).

The Web Beans RI must delegate JTA activities to the container. The SPI provides a couple hooks to easily achieve this with the TransactionServices interface.

public interface TransactionServices

{
   /**
    * Possible status conditions for a transaction. This can be used by SPI
    * providers to keep track for which status an observer is used.
    */
   public static enum Status
   {
      ALL, SUCCESS, FAILURE
   }
   /**
    * Registers a synchronization object with the currently executing
    * transaction.
    * 
    * @see javax.transaction.Synchronization
    * @param synchronizedObserver
    */
   public void registerSynchronization(Synchronization synchronizedObserver);
   /**
    * Queries the status of the current execution to see if a transaction is
    * currently active.
    * 
    * @return true if a transaction is active
    */
   public boolean isTransactionActive();
}

The enumeration Status is a convenience for implementors to be able to keep track of whether a synchronization is supposed to notify an observer only when the transaction is successful, or after a failure, or regardless of the status of the transaction.

Any javax.transaction.Synchronization implementation may be passed to the registerSynchronization() method and the SPI implementation should immediately register the synchronization with the JTA transaction manager used for the EJBs.

To make it easier to determine whether or not a transaction is currently active for the requesting thread, the isTransactionActive() method can be used. The SPI implementation should query the same JTA transaction manager used for the EJBs.

Per il corretto funzionamento al di fuori dell'implementazione delle API, ci sono un numero di requisiti che Web Beans RI pone nel container.

Isolamento del classloader

Se si integra Web Beans in un ambiente che supporta il deploy di applicazioni, occorre abilitare, automaticamente o attraverso la configurazione utente, l'isolamento del classloader per ogni applicazione Web Beans.

Servlet listener e filtri

Se si integra Web Beans in un ambiente Servlet occorre registrare org.jboss.webbeans.servlet.WebBeansListener come Servlet listener, o automaticamente, o attraverso una configurazione utente, per ciascuna applicazione Web Beans che usa Servlet.

Se si integra Web Beans in un ambiente Servlet occorre registrare org.jboss.webbeans.servlet.ConversationPropagationFilter come Servlet listener, o automaticamente, o attraverso una configurazione utente, per ciascuna applicazione Web Beans che usa JSF. Questo filtro può venir registrato in modo sicuro per tutti i deploy dei servlet.

Session Bean Interceptor

Se si integra Web Beans in un ambiente EJB occorre registrare org.jboss.webbeans.ejb.SessionBeanInterceptor come interceptor EJB per ogni EJB dell'applicazione, o automaticamente o attraverso una configurazione utente, per ciascuna applicazione Web Beans che utilizza bean enterprise.

webbeans-ri.jar

Se si integra Web Beans in un ambiente che supporta il deploy di applicazioni, occorre inserire webbeans-ri.jar nel classloader isolato delle applicazioni. Non può essere caricato da un classloader condiviso.