Ejb3 In Action Cap2

2.1 New features: simplifying EJB

2.1.1 Replacing deployment descriptors with annotations

Le annotazioni sono state una rivoluzione in fatto di semplicità del codice. Sono usate per attaccare informazioni (dette attributi).
Si possono mischiare i deployment descriptor con le annotazioni ma si ricorda che in caso di sovrapposizioni i primi (l'xml) vince sui secondi (le annotazioni) che vengono sovrascritte.
Le Common metadata annotations sono annotazioni specifiche degli EJB
annotations.jpg

2.1.2 Introducing dependency injection

Ogni componente necessita di comunicare con altri e di implementare funzionalità. L'obbiettivo primario della dependency injection è quello di fare interdipendenza di componenti con il più basso accoppiamento possibile. In pratica significa che un componente ne chiama un altro attraverso un interfaccia e che i componenti sono tenuti insieme attraverso impostazioni di configurazione e non attraverso il codice.
In EJB2 questo veniva fatto largamente attraverso JNDI. Come si vede anche in figura JNDI ritrova le risorse e i componenti che necessita. Con il risultato che il nome delle risorse sono hard-coded nel bean.
Con la DI (dependency injection) il container legge il target del bean e inietta tutto a run-time.
dip-inject-vs-lookup.jpg
In questo modo si può cambiare configurazione agilmente, la complessità va a carico del container.

2.2 Introducing the ActionBazaar application

Qui vi è un esempio dell'Action Bazaar implementato con il modello a 4 livelli.
actionBazar.jpg

2.3 Building business logic with session beans

2.3.1 Using stateless beans

Ecco un esempio di stateless

package ejb3inaction.example.buslogic;
import javax.ejb.Stateless;
import ejb3inaction.example.persistence.Bid;
@Stateless
public class PlaceBidBean implements PlaceBid {
    ...
    public PlaceBidBean() {}
    public Bid addBid(Bid bid) {
        System.out.println("Adding bid, bidder ID=" +
                                          bid.getBidderID()
                   + ", item ID=" + bid.getItemID() + ",
                                            bid amount="
                   + bid.getBidAmount() + ".");
        return save(bid);
    }
    ...
}
...
package ejb3inaction.example.buslogic;
import javax.ejb.Local;
import ejb3inaction.example.persistence.Bid;
@Local
public interface PlaceBid {
    Bid addBid(Bid bid);
}

Grazie alla notazione @Stateless che dice al container EJB che la classe (POJO) è un session bean. Il container automaticamente fornisce automaticamente servizi al bean quali il controllo automatico della concorrenza, il thread safety, pooling, and transaction management. In aggiunta si possono inserire altri servizi per i quali gli stateless bean sono elegibili quali transparent security and interceptors.
Il bean implementa un'interfaccia di tipo local.
La notazione @Local dice si può accedere localmente attraverso l'interfaccia. Se invece l'interfaccia è di tipo @Remote vengono usati Java Remote Method Invocation (RMI)
Per accedere a client non java come quelli di tecnologia .net si usa la notazione @WebService

2.3.2 The stateless bean client

Vediamo un semplice client che usa il codice visto sopra.

package ejb3inaction.example.buslogic;
import javax.ejb.EJB;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import ejb3inaction.example.persistence.Bid;
public class PlaceBidServlet extends HttpServlet {
    @EJB
    private PlaceBid placeBid;       
    public void service(HttpServletRequest request, HttpServletResponse response) 
    throws ServletException,IOException {
        int bidderID = Integer.parseInt(request.getParameter("bidder_id"));
    int itemID = Integer.parseInt(request.getParameter("item_id"));
          double bidAmount = Double.parseDouble(request.getParameter("bid_amount"));
          Bid bid = new Bid();
          bid.setBidderID(bidderID);
          bid.setItemID(itemID);
          bid.setBidAmount(bidAmount);
          placeBid.addBid(bid);
      ...
  }
  ...
}

C'è un'annotazione @EJB nella classe , quando il container vede l'annotazione al momento del caricamento del servlet, guarda l'EJB dietro e la variabile in modo da recuperare il riferimento , se è un oggetto @Remote lo recupera remotamente.

EJB 3 dependency injection

Se il container non interviene per risolvere la classe EJB si ottiene un nullPointerException quando proviamo a chiamare il metodo addBid . Nel libro vi è anche il vecchio modo di collegare le cose via un codice JNDI

Stateful session beans make maintaining server-side application state as easy as possible.

2.3.3 Using stateful beans

Maintaining the session

Primo il container deve assicurarsi che un clientpuò raggiungere a un bean dedicato per ricevere più di una invocazione di invocazione di metodo, nel tempo di una sessione
Secondo il mantenimento dello stato di una sessione è un enorme salto da riempire con HTTP
session, browser cookies, or hidden HTML form variables per provare a raggiungere lo stesso scopo.

@Stateful
public class PlaceOrderBean implements PlaceOrder {
    private Long bidderID;
    private List<Long> items;
    private ShippingInfo shippingInfo;
    private BillingInfo billingInfo;
    public PlaceOrderBean () {
        items = new ArrayList<Long>();
    }
    public void setBidderID(Long bidderId) {
        this.bidderId = bidderId;
    }
    public void addItem(Long itemId) {
        items.add(itemId);
    }
    public void setShippingInfo(ShippingInfo shippingInfo) {
        this.shippingInfo = shippingInfo;
    }
        public void setBillingInfo(BillingInfo billingInfo) {
        this.billingInfo = billingInfo;
    }
    @Remove
    public Long confirmOrder() {
        Order order = new Order();
        order.setBidderId(bidderId);
        order.setItems(items);
        order.setShippingInfo(shippingInfo);
        order.setBillingInfo(billingInfo);
        saveOrder(order);
        billOrder(order);
        return order.getOrderId();
    }
    ...
}
...
package ejb3inaction.example.buslogic;
import javax.ejb.Remote;
@Remote
public interface PlaceOrder {
    void setBidderId(Long bidderId);
    void addItem(Long itemId);
    void setShippingInfo(ShippingInfo shippingInfo);
    void setBillingInfo(BillingInfo billingInfo);
    Long confirmOrder();
}

Come si vede non c'è molta differenza tra sviluppare un bean stateless e statefull cambia l'annotazione iniziale in @Stateful. Ma sotto il cofano c'è una differenza enorme di come il container si comporta manipolando le relazioni del bean con il client e i valori memorizzati nelle variabili di istanza.
L'annotazione @Stateful dice anche al lato client cosa di deve aspettare dal bean.
L'annotazione @Remove è opzionale ma sotto il punto di vista delle performance del server è un'opzione critica. Questo perché rappresenta il punto di fine del workflow, se non specifichiamo questa annotazione rimarranno dei bean stateful che consumeranno risorse inutilmente finché la sessione scade e il bean viene rimosso.

2.3.4 A stateful bean client

Il bean stateful viene richiamato all'interno del web tier, ma per fare delle prove qui useremo un client esterno in modo similare al junit ecc.

package ejb3inaction.example.buslogic;
import javax.ejb.EJB;
public class PlaceOrderTestClient {
    @EJB
    private static PlaceOrder placeOrder;
    public static void main(String [] args) throws Exception {
           System.out.println("Exercising PlaceOrder EJB...");
         placeOrder.setBidderId(new Long(100));
         placeOrder.addItem(new Long(200));
         placeOrder.addItem(new Long(201));
         placeOrder.setShippingInfo(
           new ShippingInfo("123 My Sweet Home",
           "MyCity","MyState"));
        placeOrder.setBillingInfo(
            new BillingInfo("123456789","VISA","0708"));
         Long orderId = placeOrder.confirmOrder();
        System.out.println("Order confirmation number: " + orderId);
    }
}

Non ci sono differenze per un client nell'usare EJB Stateful o Stateless l'@EJB annotation is injecting a remote EJB into a standalone client. This is accomplished by running the client in the application client container (ACC).
L'application client container è un mini JavaEE container che può essere lanciato da linea di comando, è una souped-up JVM con l'aggiunta di Java EE juice. L'ACC riconosce e processa la maggior parte delle annotazioni Java EE .
Per far funzionare l'ACC serve un metodo main , il client tipicamente sta dentro un jar file che deve contenere una MainClass dentro un Manifest file. Opzionalmente il jar può contenere un deployment descriptor (application-client-xml) e una jndi.properties che contiene le proprietà dell'ambiente per connettersi a un container EJB. Questo è un esempio di come viene lanciato un client dentro GlassFish
appclient -client chapter2-client.jar

Purtroppo nel libro mancano ulteriori dettagli di come costruire questo client mi sa che sono nel 3.4

2.4 Messaging with message-driven beans

Processano messaggi indiretti. C'è qualcosa in messo tra chi spedisce e chi riceve questo viene detto middleman. In Java EE si standardizza il messaging attraverso l'API JMS, si vedrà più nel dettaglio nel capitolo 4.

2.4.1 Producing a billing message

Ecco un esempio di codice:

package ejb3inaction.example.buslogic;
...
import  javax.annotation.Resource;
import  javax.ejb.Remove;
import  javax.ejb.Stateful;
import  javax.jms.*;
...
@Stateful
public class PlaceOrderBean implements PlaceOrder {
   // Injects JMS resources 
   @Resource(name="jms/QueueConnectionFactory")
    private ConnectionFactory connectionFactory;
    @Resource(name="jms/OrderBillingQueue")
    private Destination billingQueue;
    ...
    @Remove
    public Long confirmOrder() {
         Order order = new Order();
         order.setBidderId(bidderId);
         order.setItems(items);
          order.setShippingInfo(shippingInfo);
      order.setBillingInfo(billingInfo);
      saveOrder(order);
      billOrder(order);
      return order.getOrderId();
  }
  ...
  private billOrder(Order order) {
      try {
    /***    Contains JMS setup code  ***/
          Connection connection = connectionFactory.createConnection();
          Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
          MessageProducer producer = session.createProducer(billingQueue);
    /*****/          
    /*** Creates and sends the message ***/
    ObjectMessage message = session.createObjectMessage();
    message.setObject(order);
    producer.send(message);
          /*****/        
 
    /***  Releases JMS resources ***/
    producer.close();
    session.close();
        connection.close();
      } catch(Exception e){
          e.printStackTrace();
      }
  }
}

Non c'è da sorprendersi che il codice mandato con il messaggio è pesantemente dipendente dalle API JMS. Il risultato è che il codice nuovo creato è mandato come un messaggio alla destinazione jms/OrderBillingQueue , il codice mancante viene iniettato con l'annotazione @Resource.
L'annotazione @EJB è usata per iniettare EJBs , con l'annotazione @Resource invece si può iniettare qualunque cose perchè è più general pourpose.
Come si vede nel listato il container cerca la risorsa JMS attraverso il parametro name e inietta dentro la variabile di istanza specifica, in questo caso connectionFactory and billingQueue. Il valore del parametro name specifica come le risorse sono limitate al context name dell'ambiente dell'EJB.
Nella parte di setup code si stabilisce una connessione al provider JMS , si crea la sessione e un message producer.
Nella fase di creazione e spedizione del messaggio la send non aspetta che dall'altra parte il messaggio sia ricevuto, perchè questa cosa viene svolta dal messaging server. Questo realizza un basso accoppiamento.
Alla fine si chiudono le risorse.
In mezzo tra sender e receiver vi è una coda.

2.4.2 Using the order billing message processor MDB

Vediamo ora il ricevente l'MDB (Message Developing Bean), ecco il codice.

package ejb3inaction.example.buslogic;
import  javax.ejb.MessageDriven;
import  javax.ejb.ActivationConfigProperty;
import  javax.jms.Message;
import  javax.jms.MessageListener;
import  javax.jms.ObjectMessage;
import  ejb3inaction.example.persistence.Order;
import  ejb3inaction.example.persistence.OrderStatus;
 
@MessageDriven( //Marks POJO as MDB
/* Specifies JMS  destination to get  messages from */
    activationConfig = {                                                          
         @ActivationConfigProperty(
             propertyName="destinationName",
             propertyValue="jms/OrderBillingQueue")
    }
)
/** Implements javax.jms.MessageListener interface **/
public class OrderBillingMDB implements MessageListener {
    ... 
    public void onMessage(Message message) {
        try {
            ObjectMessage objectMessage = (ObjectMessage) message;
            Order order = (Order) objectMessage.getObject();
                 try {
                    bill(order);
                     notifyBillingSuccess(order);
                     order.setStatus(OrderStatus.COMPLETE);
             } catch (BillingException be) {
                 notifyBillingFailure(be, order);
                 order.setStatus(OrderStatus.BILLING_FAILED);
             } finally {
                 update(order);
             }
              } catch (Exception e) {
              e.printStackTrace();
              }
  }
      ...
}

Gli MDBs sono dei session beans, non mantengono lo stato. L'annotazione @MessageDriven è l'equivalente della notazione @Stateless e @Stateful. Le proprietà della configurazione di attivazione annidata dentro la notazione @MessageDrivern dice al container cosa la destinazione JMS dell'MDB vuole ricevere come messaggi e da chi ??non sono sicuro di questa traduzione ?? "The activation configuration properties
nested inside the @MessageDriven annotation tells the container what JMS destination the MDB wants to receive messages from"
È il container che si prende cura di ricevere i messaggi e recapitarli all' MDB .
Invece di implementare una interfaccio locale o remota come i session bean si implementa l'interfaccia javax.jms.MessageListener il container usa questa per richiamare l'MDB .
Il metodo onMessage definito nell'interfaccia ha un parametro singolo javax.jms.Message che il container usa per passare un messaggio ricevuto all'MDB.

2.5 Persisting data with EJB 3 JPA

Non lo faccio conosco qualcosa in più rispetto a un'introduzione.

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