SeamFramework.orgCommunity Documentation

Capitolo 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

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.