Start - Publikationen - Wissen - TOGAF - Impressum -

Das Programmiermodell ab EJB 3.0


Die wichtigsten Neuerungen bei EJB3.0:

  • Annotations, die Angaben in den Deploymentdeskriptoren ergänzen oder überschreiben diese Angaben
  • convention over declaration
  • Verzicht auf das Homeinterface bei Session Beans
  • Wegfall des PortableRemoteObject.narrow nach JNDI lookup, direkter Cast ist nun erlaubt

Stateless Session Beans


Die Beanklasse:

@Stateless
public class FortuneBean implements FortuneRemote {
  public String getFortune () {
    // impl
  }
  :
}

Das Remote Interface der Beanklasse:

@Remote                            <- @Local für Local Interfaces
public interface FortuneRemote {
  public String getFortune();
  :
}

Es kann alternativ ein indirekter Interfacebezug geknüpft werden, wenn die Beanklasse nicht vom Remote/Local Interface erben soll:

@Stateless
@Remote(FortuneRemote.class)
public class FortuneBean {
  public String getFortune() {
    // impl
  }
  :
}

Stateless Session Beans müssen einen öffentlichen Standardkonstruktor besitzen.

Injektion von Abhängigkeiten


Variablen: die folgende Anweisung stellt in der Bean ein Feld 'someResource' zur Verfügung und füllt es, wenn möglich mit der entsprechenden Varaible aus der ejb-jar.xml

@Resource(name="SomeResource") String someResource;
:
if (someResource != null) {
   // impl
}

Sessionkontext:

@Resource private SessionContext sessionContext;
oder
@Resource
public void setSessionContext(SessionContext ctx) {
  // do something with ctx  
}
//
@Resource (name="myDB")
DataSource dataSource;
//
@EJB
UserBean userBean; 

Lebenszyklus einer Stateless Session Bean


1. Class.newInstance() -> Aufruf des Standardkonstruktors
2. Injektion der Resourcen
3. @PostConstruct
beliebig oft Businessmethode ausführen
4. @PreDestroy
5. nicht existent

Callback Methoden für die Status:

@PostConstruct
public void initialize() { <- Name beliebig, void, keine Exceptions
  // impl
}
//
@PreDestroy
public void cleanup() { <- Name beliebig, void, keine Exceptions
  // impl
}

alternativ dazu aber auch Angaben in der ejb-jar.xml möglich:

<ejb-jar>
  <enterprise-bean>
    <session>
      <ejb-name>FortuneBean</ejb-name>
      <post-construct>
        <lifecycle-callback-method>initialize</lifecycle-callback-method>
      </post-construct>
      <pre-destroy>
        <lifecycle-callback-method>cleanup</lifecycle-callback-method>
      </pre-destroy>
      :

Der Client einer Stateless Session Bean


Im Gegensatz zu EJB 2.1 braucht er kein Home-Interface, er kann direkt das Remote Interface der Bean adressieren. Zusammen mit dem Wegfall von PortableRemoteObject.narrow sieht der Client Code mit EJB3.0 sehr einfach aus:

try {
  Context ctx = new InitalContext();
  Object ref = ctx.lookup("FortuneBean");
  FortuneRemote fortune = (FortuneRemote) ref;
  :
} catch (NamingException e) {
  // well..
}

Zur Erinnerung: der gleiche Code mit EJB2.1:

try {
  Context ctx = new InitalContext();
  Object homeObj = ic.lookup("FortuneEJB");
  FortuneHome fortuneHome = (FortuneHome) 
    PortableRemoteObject.narrow(homeObj, FortuneHome.class);  
  FortuneRemote fortune = fortuneHome.create(...);
  :
} catch (NamingException e) {
  // well..
}

Das Codebeispiel funktioniert für einen Stand-alone Client, sein InitialContext wird von der Serverimplementierung bereitgestellt. Dazu sind die dafür vorgesehenen Serverbibliotheken im Classpath des Clients aufzunehmen. Für den GlassFish wäre das die appserv-rt.jar. Der Java SE JNDI Bootstrap sucht dann automatisch nach einer jndi.properties und benutzt die dort gemachten Angaben. Eventuell muss man dann nur noch die Standardwerte für den JNDI Host (localhost) und Port (3700) überschreiben, zum Beispiel durch setzen entsprechender Startparameter:

-Dorg.omg.CORBA.ORBInitialHost=JNDI_host_name
-Dorg.omg.CORBA.ORBInitialPort=JNDI_host_port

Bringt der Client seine eigene JNDI Implementierung mit (er ist zum Beispiel eine Tomcat Instanz), müssen die JNDI-Parameter selber bereitgestellt werden. Dazu konstruiert man einen InitialContext mit entsprechendne Argumenten, die abhängig vom verwendeten JNDI Server sind:

Properties props = new Properties();
// InitialContext Properties für den Aufruf eines GlassFish JNDI:
props.setProperty("java.naming.factory.initial", "com.sun.enterprise.naming.SerialInitContextFactory");
props.setProperty("java.naming.factory.url.pkgs", "com.sun.enterprise.naming");
props.setProperty("java.naming.factory.state", "com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
props.setProperty("org.omg.CORBA.ORBInitialHost", "localhost"); // default!
props.setProperty("org.omg.CORBA.ORBInitialPort", "3700"); // default!
//
InitialContext initialContext = new InitialContext(props);
:

Der Code für die EJB und ihren Client steht zur freien Verfügung. Bei Problemen mit dem JNDI lookup sollten Sie zunächst einen JNDI Browser zu Rate ziehen - jedes moderne Java EE Server hat einen solchen. Beim GlassFish findet man ihn etwas versteckt unter 'Server Admin':

Stateful Session Bean


Alles für die Stateless Session Beans genannte trifft auch hier zu. Besonderheiten gegenüber einer Stateless Session Bean:

  • Der Lebenszyklus der Bean ist komplexer und beinhaltet @PrePassivate und @PostActivate. Hier ist das Vorgehen in Analogie zu @PostConstruct und @PreDestroy zu sehen. Die Implementierung entspricht inhaltlich der von ejbPassivate und ejbActivate in EJB 2.1.
  •     [  Not Exists   ]
        |               A
        |Class.getInstance()
        |@PostConstruct |
        |               |@PreDestroy
        V               |
        [  Method Ready ] -> Exception -> [Not Exists]
        |               A
        |@PrePassivate  |
        |               |@PostActivate
        V               |
        [    Passive    ] -> Timeout -> [Not Exists]
    
Der Client braucht eine Möglichkeit, die Beaninstanz freizugeben. Dazu ruft er eine mit @Remove annotierte Methode auf (Implementierung in Analogie zu ejbRemove, aber nun sind auch Parameter erlaubt).
@Remove(retainIfException=true) <- Bei ApplicationException bleibt Bean erhalten
public void removeBean() {
  // impl
}

Danach steht dem Client die Bean nicht mehr zur Verfügung. Der Aufruf von @Remove ist für Stateless Session Beans enorm wichtig, damit der Server alle den Zustand betreffenden Ressourcen freigeben kann und muss vom Client so früh wie möglich erfolgen.

Der Client muss sich gesondert um die Initialisierung der Bean kümmern. In EJB 2.1 wurden dazu die verschiedenen create Methoden des Home Interface gerufen. Diese Möglichkeit steht auch noch in EJB 3.0 zur Verfügung:

@Stateful
@RemoteHome(FortuneRemoteHome.class)
public class FortuneBean {
  @Init <- Erzwungen
  public void create(String ini) {
    // impl
  }
  :
}
//
public interface FortuneRemoteHome extends EJBHome {
  public FortuneRemote create(String ini) <- beliebige Argumente
    throws CreateException, RemoteException;
}

Viel einfacher jedoch ist es, den Aufruf von Business-Methoden als Initialisierung vorzusehen. Erst nach einem solchen Aufruf befindet sich die Bean in einem fachlich korrekten Zustand. Ein Beispiel wäre eine logon Methode, die einmal mit den richtigen Parametern (Nutzer-ID, Passwort) zu rufen ist, bevor sie andere Methodenaufrufe zulässt.

Der Client einer Stateful Session Bean


Genau wie bei Stateless Session Beans erfolgt ein lookup direkt auf das Remote/Local Interface der Bean. Der Client ist dann für die korrekte Initialisierung der Bean verantwortlich (da nun keine create-Methoden eines Home Interface zur Verfügung stehen) oder man adressiert mit @Init eine solche.

Message Driven Beans


Ein MDB hat nur eine Business-Methode, die auch nie von einem Client direkt gerufen wird: onMessage. Die Konfiguration des MessageListener Verhaltens erfolgt zur Entwicklungszeit in Form von Key-Value Paaren in @MessageDriven oder alternativ zur Deployzeit in der ejb-jar.xml.

@MessageDriven(activationConfig={
  @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
  @ActivationConfigProperty(propertyName="destination", propertyValue="queue/SomeQueue"),
  @ActivationConfigProperty(propertyName="acknowledgeMode", propertyValue="Auto-acknowledge"),
  :
})
public class FortuneBean implements MessageListener {
  @Resource
  MessageDrivenContext context;
  //
  public void onMessage(Message message) {
    // impl
  }
}

Der Lebenszyklus entspricht dem einer Stateless Session Beans

Der Client einer Message Driven Bean


Der Client kann die onMessage Methode nicht direkt erreichen, er sendet eine Nachricht an die Queue an die die Bean lauscht:

try {
  // -Djava.naming.factory.initial=
  // -Djava.naming.factory.urls.pkgs=
  // -Djava.naming.factory.url=
  Context ctx = new InitalContext();
  //
  ConnectionFactory factory = (ConnectionFactory) ctx.lookup("ConnectionFactory");
  Queue queue = (Queue) ctx.lookup("queue/SomeQueue");
  //
  Connection con = factory.createConnection();
  Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE);
  MessageProducer sender = session.createProducer(queue);
  //
  TextMessage msg = session.createTextMessage();
  msg.setText(...);
  :
  sender.send(msg);
  :
} catch (NamingException e) {
  // well..
}
copyright © 2002-2018 | Dr. Christian Dürr | prozesse-und-systeme.de | all rights reserved