CERCA SITEMAP FEED RSS 1280
Ultimo aggiornamento: 30 Agosto 2009

L'interfaccia EJBContext e la dependency injection (DI)

Enterprise JavaBeans è basato sull’idea di oggetti gestiti che non sono altro che Plain Old Java Object (POJO) annotati.
Quando un client invoca un EJB utilizzando l’interfaccia che questo espone, non dialoga direttamente con l’istanza ma con il container che fa da proxy fra le due parti.
In particolare per ogni istanza di un EJB, il container genera un proxy chiamato EJB object che ha accesso a tutte le funzionalità del container e ai servizi che questo offre a supporto.
Dal momento che tutte le richieste rivolte all’EJB passano attraverso il proxy, questo può aggiungere i servizi del container necessari.
Tutto ciò avviene in maniera del tutto trasparente al client e allo sviluppatore: nel caso di session bean il client interagisce con il proxy mediante l’interfaccia business, nel caso di MDB il proxy è collocato tra il message provider e l’istanza del bean.

L'interfaccia EJBContext

Nel caso ideale i componenti EJB dovrebbero incorporare esclusivamente la logica business ed evitare l’accesso al container o l’uso diretto dei servizi da questo offerti.
Talvolta però è necessario per il bean accedere esplicitamente ai servizi offerti dal container: l’interfaccia javax.ejb.EJBContext costituisce il punto di accesso ai servizi offerti dal container che sono normalmente definiti in fase di configurazione e gestiti completamente dal container.

L’interfaccia EJBContext è così definita:
public interface EJBContext 
{
  public Principal getCallerPrincipal();
  public boolean isCallerInRole(String roleName);
  public EJBHome getEJBHome();
  public EJBLocalHome getEJBLocalHome();
  public boolean getRollbackOnly();
  public UserTransaction getUserTransaction();
  public void setRollbackOnly();
  public TimerService getTimerService();
  public Object lookup(String name);
}
Fra questi metodi:
  • getEJBHome e getEJBLocalHost sono entrambi opzionali e usati solo per garantire compatibilità a ritroso con EJB 2.1
  • getCallerPrincipal e isCallerInRole sono utilizzati per aspetti relativi alla sicurezza, e consentono di ottenere l’identità del client e verificare se questo possiede un determinato ruolo.
  • getRollbackOnly e setRollbackOnly sono utilizzati per gestire le transazioni nel caso di container-manager transaction (CMT) e servono a marcare una transazione per il roll back o verificare se è stata marcata per il rollback.
  • getTimerService consente di ottenere accesso ad un servizio di timer EJB.
  • lookup consente di effettuare il lookup di una risorsa memorizzata nel registro JNDI
Session e message-driven bean hanno le loro sottoclassi di EJBContext ovvero SessionContext e MessageDrivenContext.

Accesso all'EJBContext

E’ possibile accedere a EJBContext mediante Dependency Injection come mostra il seguente esempio:
@Stateless
public class PlaceBidBean implements PlaceBid 
{
  @Resource
  SessionContext context;

  ...
}
In questo caso quando il conteiner individua l’annotazione @Resource sulla variabile context di tipo SessionContext assegna alla variabile l’istanza del SessionContext del bean stateless.
SessionContext aggiunge un certo numero di metodi come getBusinessObject, getEJBLocalObject, getEJBObject, getInvokeBusinessInterface e getMessageContext anche se sono utilizzati raramente (in particolare getEJBLocalObject e getEJBObject sono utilizzati per bean EJB2 e usati con bean EJB 3 produrranno un’eccezione).
MessageDrivenContext non aggiunge metodi e genera un’eccezione se i metodi isCallaerInRole, getEJBHome o getEJBLocalHome sono invocati (i MDB non hanno un’interfaccia business e non possono essere invocati direttamente dal client).
@MessageDriven
public class MioMDB 
{
  @Resource MessageDrivenContext context;
  ...
}

Dependency injection mediante annotazione @Resource

L’annotazione @Resource rappresenta il meccanismo più versatile di Dependency Injection in EJB 3.

Il seguente codice effettua l’injection di un DataSource in un session bean stateless:
@Stateless
public class MioBean implements MiaInterfaccia
{
  ...
  @Resource(name="jdbc/mioDB")
  private javax.sql.DataSource dataSource;
In questo caso il container non deve fare molto per fare l’injection della risorsa perché il parametro name che indica il nome JNDI della risorsa di cui occorre fare l’injection, è presente.
Il container risolve il riferimento alle risorse durante il deployment, se la risorsa non viene trovata, il container restituisce una runtime exception e il bean diventa inutilizzabile.

L’annotazione @Resource è così definita:
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface Resource 
{
  public enum AuthenticationType 
  {
    CONTAINER,
    APPLICATION
  }
  String name() default "";
  Class type() default Object.class;
  AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
  boolean shareable() default true;
  String mappedName() default "";
  description() default "";
}
Come è possibile notare è possibile applicarla non solo alle variabili ma anche ai metodi set di un bean.
Il fatto che EJB 3 interpreti i nomi JNDI come riferimenti locali costituisce una valida alternativa al dover di volta in volta menzionare l’Environment Naming Context locale (java:comp/env).
Questo è utile finchè non si usano nomi globali, se ciò accade occorre accertarsi che il mapping fra ENC e i nomi JNDI globali avvenga correttamente

Per comprendere come funziona la DI applicata ai metodi set analizziamo il seguente esempio:
@Stateless
public class Miobean implements MiaInterfaccia
{
  ...
  private DataSource dataSource;

  ...

  @Resource(name="jdbc/MioDB")
  public void setDataSource(DataSource dataSource) 
  {
    this.dataSource = dataSource;
  }

  public DataSource getDataSource() 
  {
    return dataSource;
  }
}
Quando il container trova l’annotazione @Resource prima del metodo setDataSource, effettua il lookup della risorsa da JNDI utilizzando il valore del parametro name e quindi chiama il metodo setDataSource passando come parametro la risorsa recuperata.

E’ quindi possibile effettuare una connessione al database non appena viene effettuata l’injection:
private DataSource dataSource;
private Connection connection;
...
@Resource(name="jdbc/actionBazaarDB")
public void setDataSource(DataSource dataSource) 
{
  this.dataSource = dataSource;
  this.connection = dataSource.getConnection();
}
Il parametro type dell’annotazione @Resource può essere utilizzato per specificare il tipo della risorsa iniettata.
@Resource(name="jdbc/actionBazaarDB",type=javax.sql.DataSource.class)
private DataSource dataSource;
Se questa viene omessa si utilizza il tipo della variabile nella quale la risorsa viene iniettata.

Altri possibili parametri per l’annoazione @Resource sono:
  • authenticationType: il valore CONTAINER (valore di default) indica che per la risorsa si usa il security context del container, il valore APPLICATION significa che l’autenticazione per la risorsa è fornita dall’applicazione. shareable: specifica se la risorsa può essere condivisa
  • description: contiene una descrizione della risorsa
  • mappedName: un nome vendor-specific nel quale la risorsa potrebbe essere mappata.

Uso dell'annotazione @Resource

La dependency injection può essere utilizzata esclusivamente all’interno di oggetti gestiti dal container.
Negli altri casi (es. classi di utilità) è possibile ricorrere alla classica operazione di lookup.

Occorre referenziare la risorsa dalla classe EJB:
@Resource(name="jdbc/mioDB",mappedName="jdbc/mioDS", type=javax.sql.DataSource.class)
@Stateless
public class MioBean implements MiaInterfacciaBean
e successivamente fare il lookup dalla classe di utilità:
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/mioDB")

Accesso a risorse JMS

Se l’applicazione fa uso di Java Mesasging Service (JMS) allora potrebbero essere necessarie le risorse javax.jms.Queue, javax.jms.Topic, javax.jms.QueueConnectionFactory o javax.jms.TopicConnectionFactory.
Tali risorse che sono memorizzate nel contesto JNDI dell’application server, possono essere ottenute mediante l’annotazione @Resource nel seguente modo:
@Resource(name="jms/actionBazaarQueue")
private Queue queue;

Accesso alle env-entry

In alcuni casi alcuni parametri dell’applicazione cambiano da un deployment all’altro.
E’ possibile usare l’annotazione @Resource per accedere alle entry specificate nel deployment descriptor (elemento env-entry):
@Resource
private boolean miaentry;
oppure
@Resource(name="miaentry")
private boolean miaentry;
<env-entry>
  <env-entry-name>miaentry</env-entry-name>
  <env-entry-type>java.lang.Boolean</env-entry-type>
  <env-entry-value>true</env-entry-value>
</env-entry>
I valori possibili per il tag env-entry-type sono String, Character, Byte, Short, Integer, Long, Boolean, Double, and Float.

Accesso a risorse mail

Oltre ai data source JDBC e alle risorse JMS è possibile accedere medainte @Resource ad altre risorse quali ad esempio sessioni javax.mailSession utilizzate per inviare email
@Resource(name="mail/MiaSessione")
private javax.mail.Session mailSession;

Accesso ai servizi di timer

I servizi di timer consentono agli EJB di schedulare task.
E’ possibile anche in questo caso effettuare la dependency injection della risorsa mediante l’annotazione @ Resource:
@Resource
javax.ejb.TimerService timerService;

Ereditarietà

Quando un EJB estende un altro EJB o un POJO, se la superclasse definisce delle dependency injection mediante l’annotazione @Resource queste vengono ereditate dalla sottoclasse
@Stateless
public class Superclasse implements InterfacciaSuperclasse
{
  @Resource
  private boolean miaentry;
  ...
}

@Stateless
public class Sottoclasse extends Superclasse implements InterfacciaSottoclasse
{
  ...
}

Looking up di risorse e EJB

Per effettuare il lookup di qualsiasi oggetto memorizzato in JNDI si può usare il metodo EJBContext.lookup.
Utilizzando tale metodo è possibile ottenere dinamicamente (a runtime) la risorsa che si vuole utilizzare (basta specificare il nome JNDI della risorsa come parametro del metodo).