Creazione Session Bean

Sorgente Articoli su html.it

Stateless

Primo stateless session bean

Creazione del primo stateless session bean, partiamo dall'interfaccia:

package it.html.ejb3.pricer;
 
import it.html.ejb3.persistence.Item;
import javax.ejb.Local;
 
@Local
public interface StatelessSessionPricerTaxLocal {
  double calculateTax();
}

Abbiamo creato una semplice interfaccia Java, annotandola con l'annotazione @Local. Ciò vuol dire che abbiamo intenzione di utilizzare questa classe all'interno dello stesso container. Qualora avessimo voluto esporre il servizio come interfaccia remota, avremmo dovuto annotarla con l'annotazione @Remote. Lo stesso se vogliamo predisporre entrambe le interfacce.

Poi andiamo a concretizzare

package it.html.ejb3.pricer;
 
import it.html.ejb3.persistence.Item;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.ejb.Stateless;
 
//Stateless Session Bean utilizzato per definire la politica sui prezzi
//da applicare. La creazione avviene mediante l'uso delle opportune annotazioni.
@Stateless
public class StatelessSessionPricerTaxBean implements StatelessSessionPricerTaxLocal {
 
  //Recuperiamo il valore della variabile (algorithmToUse) dal descrittore di deploy
  //a tempo di deploy. Serve per definire quale algoritmo utilizzare.
  @Resource(name="algorithmToUse")
  public String algorithm;
 
  //Il metodo ha una logica molto semplice, in base all'algoritmo indicato
  //restituiamo un valore numerico, indicante una percentuale
  public double calculateTax() {
    if (algorithm!=null && algorithm.equals("easy"))
      return 0.15;
    if (algorithm!=null && algorithm.equals("hard"))
      return 0.25;
    //default medium
    return .20;
  }
}

La classe (che implementa l'interfaccia locale, o remota, o entrambe) viene annotata con @Stateless, indicando chiaramente che questa classe é un bean di tipo Stateless. Il metodo esposto nell'interfaccia viene implementato (in questo caso la logica é molto semplice). Utilizzare delle risorse all'interno del bean diventa facile con le annotations. In pratica, lo strumento ci consente di marcare delle risorse che a tempo di deploy verranno configurate opportunamente. Notate nel nostro caso l'attributo algorithm, che viene marcato dall'annotazione @Resource(name=…): stiamo dicendo al container che quella variabile dovrà essere valorizzata con il parametro "algorithmToUse" contenuto nel descrittore di deploy. In questo modo potremo modificare il comportamento del componente modificando solo il file XML, senza accedere in alcun modo al codice.

Secondo Stateless Session Bean

Interfaccia:

package it.html.ejb3.pricer;
import it.html.ejb3.persistence.Item;
import javax.ejb.Local;
 
@Local
public interface StatelessSessionPricerLocal {
  double getPrice(Item prod) throws it.html.ejb3.persistence.ItemNotFoundException;
}

Il bean (stateless) calcola il prezzo finale di un oggetto passato come riferimento

package it.html.ejb3.pricer;
 
import it.html.ejb3.persistence.Item;
import it.html.ejb3.persistence.ItemNotFoundException;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Stateless;
 
@Stateless
public class StatelessSessionPricerBean implements StatelessSessionPricerLocal {
 
  //Recuperiamo l'EJB utilizzato per il calcolo delle tasse
  @EJB
  private StatelessSessionPricerTaxLocal taxEngine;
 
  //La classe che simula il price engine
  private PriceEngine engine;
 
  //Il metodo verrà chiamato subito dopo il costruttore, valorizzando le variabili di istanza presenti
  @PostConstruct
  private void postConstruct(){
    engine=new PriceEngine();
  }
 
  //Il metodo di logica, per recuperare il prezzo dell'oggetto passato come riferimento
  public double getPrice(Item prod) throws ItemNotFoundException {
    //Inoltriamo la richiesta al layer, aggiungendo il valore della tassa al momento della richiesta
    return engine.getPrice(prod)*(1+(taxEngine.calculateTax()));
  }
}

La classe concreta viene annotata come @Stateless implementando l'interfaccia locale. Per poter implementare il metodo di logica abbiamo bisogno del PriceEngine e del bean visto in precedenza. Vediamo le annotazioni in azione: attraverso l'annotazione @EJB diamo un riferimento del bean che vogliamo usare. Nessun uso di Context (come avveniva in EJB 2.0) o altri strumenti. Semplicemente la definizione dell'interfaccia da utilizzare, annotata con l'opportuna annotation.

Potrete chiedere tutti i servizi dell'application server in maniera immediata, Datasource, code, servizi di messaggistica e tutto quanto vi può essere utile, direttamente dal codice, senza dover configurare file XML o altri sistemi di configurazione. Immediato e semplice, no? Inoltre anche la lettura del codice ne beneficia, che come vediamo si commenta anche grazie l'uso delle annotazioni.

La creazione della classe PriceEngine viene delegata nel metodo postConstruct(). Banalmente inizializziamo la classe. La cosa interessante é notare l'annotazione usata per il metodo @PostConstruct. Attraverso quest'annotazione stiamo dicendo al container, che, seguendo il ciclo di vita del componente, subito dopo il costruttore, dovrà essere richiamato il metodo postConstruct().

Attraverso l'annotazione dei metodi possiamo dunque gestire direttamente dal codice l'intero ciclo di vita del bean: @PreActivate, @PostActivate, @PostConstruct… esistono decine di annotazioni, usando le quali riuscirete a seguire il ciclo di vita del componente. In pratica, le annotazioni, in questo caso, sostituiscono la classe callBack handler, gestendo direttamente il comportamento del bean. Ovviamente, tali metodi possono non essere implementati.

La logica del metodo, a questo punto, é un'operazione tra l'engine appena recuperata e l'altro EJB a cui il bean fa riferimento

Statefull

package it.html.ejb3.search;
 
import java.util.Collection;
import javax.ejb.Local;
 
@Local
public interface StatefulSessionSearchLocal {
  Collection searchAll();
  Collection getLastSearched();
  Collection search(String title);
}

Il bean (stateful) si occupa di mantenere una struttura dati contenente le ultime

package it.html.ejb3.search;
 
import it.html.ejb3.persistence.PersistenceLayer;
import java.util.Collection;
import java.util.Queue;
import java.util.Stack;
import javax.annotation.PostConstruct;
import javax.ejb.Stateful;
 
@Stateful
public class StatefulSessionSearchBean implements StatefulSessionSearchLocal {
 
  //Utilizziamo una struttura dati a pila, riprendendo l'implementazione java.util
  Stack<String> stack;
 
  //Il layer di persistenza
  PersistenceLayer pl;
 
  @PostConstruct
  private void postConstruct(){
    stack=new Stack<String>();
    pl=PersistenceLayer.getInstance();
  }
 
  //Restituisce tutti gli oggetti
  public Collection searchAll() {
    return pl.getList();
  }
 
  //Restituisce gli elementi, che compiono match con la stringa passata in riferimento
  public Collection search(String title) {
    //Aggiungiamo la stringa richiesta
    stack.add(title);
    //Inoltriamo la richiesta
    return pl.getList(title);
  }
 
  //Recuperiamo le ultime ricerche effettuate
  public Collection getLastSearched(){
    return stack;
  }
}

I commenti contenuti nel codice spiegano abbastanza bene il comportamento della logica di business. Notiamo che la classe viene annotata come @Stateful, in quanto questo componente é un componente che mantiene la sessione dell'utente (ricordiamo che deve registrare le ultime ricerche effettuate da ogni utente).

Servlet di gestione

Per completezza andiamo a vedere il client di questa logica applicativa. Una servlet che funge da controller che ha il compito di definire il flusso di operazioni dei nostri due casi d'uso (ricerca e visualizzazione prezzo). Come al solito racchiudiamo ognuno di questi casi d'uso in un metodo della servlet:

La servlet funge da flusso di controllo

@EJB(name="SessionSearchRef", beanInterface=StatefulSessionSearchLocal.class)
public class Controller extends HttpServlet {
 
  //Utilizziamo le annotazioni per utilizzare gli EJB
  @EJB
  private StatelessSessionPricerLocal pricer;
 
  //Il layer viene utilizzato per recuperare informazioni (simulate) di persistenza sull'oggetto
  private PersistenceLayer pl;
 
  //Manteniamo un riferimento al Context, potrebbe esserci utile
  private Context ctx;
 
  //Inizializzazione componente
  public void init(){
    try {
      ctx=new InitialContext();
    } catch (NamingException ex) {
      ex.printStackTrace();
    }
    pl=PersistenceLayer.getInstance();
  }
 
  protected void service(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException {
    //dispatch the request
    String op=request.getParameter("op");
 
    if (op!=null && op.equals("search")){
      doSearch(request,response);
    }else if (op!=null && op.equals("getPrice")){
      doGetPrice(request,response);
    }else{
      printMessage(response,"Operazione non disponibile.");
    }
  }
 
  //Flusso operazioni di ricerca
  private void doSearch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    StatefulSessionSearchLocal ssfSearch;
    Collection toRet=null;
 
    //Valorizziamo il bean con quello relativo alla sessione dell'utente che ne fa richiesta
    ssfSearch=(StatefulSessionSearchLocal) request.getSession(true).getAttribute("sessionSearch");
    if (ssfSearch==null){
      try {
        //se non é stato creato, lo creiamo
        ssfSearch=(StatefulSessionSearchLocal) ctx.lookup("java:comp/env/SessionSearchRef");
      } catch (NamingException ex) {
        printMessage(response,"Errore durante l'operazione di ricerca!");
        return;
      }
      request.getSession(true).setAttribute("sessionSearch",ssfSearch);
    }
 
    //Recupero la keyword
    String keyword=request.getParameter("keyword");
    //ed effettuo la ricerca
    if(keyword==null)
      toRet=ssfSearch.searchAll();
    else
      toRet=ssfSearch.search(keyword);
 
    System.out.println("DEBUG: "+toRet);
 
    //Salviamo l'oggetto nella request, in modo da renderlo visibile
    RequestDispatcher rd=this.getServletContext().getRequestDispatcher("/index.jsp");
    request.setAttribute("list",toRet);
    //alla pagina JSP che verrà inoltrata
    rd.forward(request,response);
  }
 
  //Flusso operazioni prezzo prodotto
  private void doGetPrice(HttpServletRequest request, HttpServletResponse response) throws IOException {
    //Cerco l'oggetto
    String pid=request.getParameter("pid");
    Item i;
    try {
      i = pl.getItem(Integer.parseInt(pid));
      //il prezzo corrente
      double price=pricer.getPrice(i);
      //e stampo l'informazione
      printMessage(response,"Il prezzo del prodotto <b>"+i.getTitle()+"</b> ... "+price+" Eu");
    } catch (NumberFormatException ex) {
      ex.printStackTrace();
    } catch (ItemNotFoundException ex) {
      printMessage(response,"Oggetto non trovato!");
    }
  }
}

In testa alla servlet definiamo il riferimento che utilizzeremo e l'interfaccia del bean. La servlet, infatti, dovrà poggiarsi al bean stateful per effettuare le ricerche ed al bean stateless per recuperare i prezzi. Il bean stateless viene recuperato mediante l'annotazione @EJB, lo stateful in maniera classica, in quanto dobbiamo preoccuparci di crearne uno per ogni diverso utente (e poi salvarlo in sessione).

manca quest'ultima pagina

Salvo diversa indicazione, il contenuto di questa pagina è sotto licenza Creative Commons Attribution-ShareAlike 3.0 License