Advanced   Java   Services Stateless Session Beans 3.0 Back Next Up Home


Einführung


Eine stateless Session Bean ist eine Bean die keine clientspezifischen Daten speichert und so eine quasibeliebige Anzahl von Clients bedienen kann. Sie hat nur die zwei Zustände "Does not Exist" bzw. "Ready". In letzterem Zustand kann Sie die Businessmethoden abarbeiten.

Eine Hello World EJB 3.0 stateless Sessionbean


Wir werden dieses Beispiel ohne Netbeans und ohne Eclipse entwickeln. Stattdessen arbeiten wir mit einem einfachen Editor, etwa mit Textpad. So wird das Zusammenspiel der einzelnen Komponenten am deutlichsten. Der Übergang zu Eclipse sollte dann kein Problem darstellen. Des weiteren verzichten wir der Einfachheit halber darauf die Javaklassen bzw. Interfaces in Packages zu legen. Als Übung können Sie das Beispiel so abändern, daß Sie packages einsetzen. In der Praxis werden die Klassen immer in packages gelegt.

Unser Beispiel besteht aus drei Dateien, dem Remoteinterface, der eigentlichen Beanklasse und dem Client der den Dienst dieser EJB über das Protokoll "iiop" (Internet Inter-ORB Protocol) in Anspruch nehmen will. Zu diesem Protokoll gibt es eine kurze Information auf Wikipedia.

Das Remoteinterface


import javax.ejb.Remote;

@Remote
public interface SayHello
{
    public String sayHello();
}

Das business-interface ist in 3.0 ein ganz normales Interface geworden. Eine Ableitung vom Interface javax.ejb.EJBObject ist nicht mehr notwendig. Wir markieren das Interface mit einer Annotation als remote interface. Zu jeder Annotation gibt es ein entsprechendes Import. Zum kompilieren braucht man das Archiv javaee.jar (siehe oben). Wir legen dieses jar-datei in das lib/ext-Verzeichnis des JDK und in das lib/ext-Verzeichnis des JRE. Für die Entwicklung brauchen wir die Datei im JDK, zum Ausführen greift dann die JVM auf die Datei im JRE zu.

Die Beanklasse


import javax.ejb.Remote;
import javax.ejb.Stateless;

// annotations ersetzen deployment descriptor (teilweise)
// deployment descriptor kann aber immer noch verwendet werden
// manche einstellungen kann man im deployment descriptor überschreiben

@Stateless(name="SayHello", mappedName="ejb/SayHelloJNDI")
@Remote(SayHello.class)

public class SayHelloBean implements SayHello
{
   String message;

   public SayHelloBean()
   {
      System.out.println("constructor SayHelloBean() called");
      message = "Hello EJB 3.0";
   }

   @Override
   public String sayHello()
   {
      return message;
   }

   // optional
   @Remove
   public void ejbRemove()       // name der methode ist jetzt beliebig
   {
      System.out.println("ejbRemove called");
   }
}

Die Beanklasse ist nun das, was man im EJB3.0 Jargon ein "POJO" nennt, ein "plain old java object". Ein Import von javax.ejb.SessionBean ist nicht mehr notwendig. Die Methoden können bei Bedarf über Annotations vereinbart werden. Zum Kompilieren braucht man das Archiv javaee.jar (siehe oben) und das Interface SayHello.class .

Erstellen des Bean-Archivs und der Deployvorgang


Das Remotinterface und die Beanklasse werden zusammen mit einem META-INF-Verzeichnis zu einer ear-Datei gepackt. Durch den vereinfachten Aufbau dieses Archivs kann es auch die Endung .jar besitzen. Das META-INF Verzeichnis enthält eine Manifestdatei MANIFEST.MF und optional einen Deploymentdescriptor im XML-Format namens ejb-jar.xml. Die beiden folgenden Screenshots zeigen den Aufbau eines ear-Archivs nach EJB 3.0 .

  


Die Datei ejb-jar.xml ist durch die Verwendung von Annotations bei einfachen Beans überflüssig. Wir nehmen trotzdem eine leere Datei auf um die vollständige Struktur des Archivs zu zeigen.

Eine leere ejb-jar.xml Datei


<?xml version="1.0" encoding="UTF-8" ?>
<ejb-jar>
    <enterprise-beans>
    </enterprise-beans>
</ejb-jar>

Noch ein "wenig" simpler ist der Aufbau der Datei MANIFEST.MF

Die Datei MANIFEST.MF


Manifest-Version: 1.0

Die Datei MANIFEST.MF ist eine ASCII-Datei und enthält tatsächlich nur den einen oben angegebenen Eintrag.

Erstellen des Archivs mit einem Packer


Da ein ear-Archiv lediglich eine zip-Datei ist, können wir unser Archiv etwa mit dem Packer "Winrar" erstellen. Wir markieren den gesamten Inhalt des Verzeichnisses "sayHelloBean" und öffnen über das Kontextmenü den Eintrag "Add to Archive". Mit zwei weiteren Mausklicks erstellt uns Winrar im selben Verzeichnis eine Datei "sayHelloBean.zip". Die Endung ".zip" ändern wir nun einfach in ".ear". Fertig.

Erstellen des Archivs mit dem Java Konsoltool jar(.exe)


Das Konsiltool jar ist dem Unix Konsoltool tar nachempunden. Wir öffnen ein Konsolfenster und begeben uns in das Verzeichnis sayHelloBean. Der Aufruf von jar cvf meineBiene.ear * erstellt dann ein Enterprisearchiv mit dem Namen meineBiene.ear.

Der Hauptname des ear-Archivs ist übrigens völlig beliebig, wir könnten unsere Datei genauso "wrzlbrmpft.ear" nennen. Bei der Verwendung von Eclipse können wir das Erstellen des Archivs zur Gänze an Eclipse delegieren.

Automatisches deployen


Das fertige Archiv bringen wir nun zum Server und legen es ins Autodeployverzeichnis von Glassfish.

<SUNHOME>/SDK/domains/domain1/autodeploy

Glassfish kontrolliert periodisch den Inhalt dieses Verzeichnis und versucht neu hinzugekommene ear-Dateien (oder jar-Dateien oder war-Dateien) einzusetzen ( to deploy = anwenden, aufstellen, einsetzen). Gelingt dies wird eine leere Datei mit dem Namen "meineBiene.ear_deployed" angelegt. Gelingt es nicht, so heißt die entsprechende Datei "meineBiene.ear_deployFailed"

Sieht man die Meldung "meineBiene.ear_deployed" auftauchen, so ist immerhin dieser Vorgang gelungen. Das heißt aber noch nicht, daß die Bean wirklich gestartet worden ist, also ein Objekt der Beanklasse (in diesem Falle SayHelloBean) angelegt worden ist. Man muß nun zum Client zurückgehen und diesen starten.

Die log-Datei server.log


Zum Austesten von Beans sollte man immer die Datei server.log im Verzeichnis <SUNHOME>/SDK/domains/domain1/logs geöffnet haben und die Einträge kontrollieren. Hier erscheinen die Fehlermeldungen falls der Deployvorgang mißlingt. Ein gelungener Deployvorgang wird folgendermaßen dokumentiert:

[#|2010-07-08T10:50:17.593+0200|INFO|sun-appserver2.1|javax.enterprise.system.core.classloading|_ThreadID=10;
_ThreadName=main;sayHelloBean;|LDR5010: All ejb(s) of [sayHelloBean] loaded successfully!|#]


Der iiop-Client


import java.util.*;
import javax.naming.*;

public class SayHelloClient
{
    public static void main(String[] args)
    {
        System.out.println("about to create initialcontext");
        String url = "iiop://<server-ip-adress>" ;
        String jndiName = "ejb/SayHelloJNDI";

        Properties env = new Properties();
        env.put("org.omg.CORBA.ORBInitialHost","<server-ip-adress>");  // default ist localhost !!
        //env.put("org.omg.CORBA.ORBInitialPort","3700");  // ist default
        env.put("java.naming.factory.initial","com.sun.enterprise.naming.SerialInitContextFactory");
        //env.put("java.naming.factory.url.pkgs","com.sun.enterprise.naming");  // hier noch nicht notwendig
        //env.put("java.naming.provider.url","com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");  // hier noch nicht notwendig
        //env.put("java.naming.factory.state",url);  // hier noch nicht notwendig

        try
        {
            Context ctx = new InitialContext(env);   // NamingException
            System.out.println("initial context received");

            SayHello remote = (SayHello)ctx.lookup(jndiName);    // NamingException
            if( remote != null)
                System.out.println("remote class = " + remote.getClass().getName() );
            else
                throw new NullPointerException("lookup failed");

            // der wrapper hat den typ _SayHello_Wrapper und hat das remote interface implementiert,
            // man kann also ohne cast einfach die businessmethoden rufen
            System.out.println(remote.sayHello());
        }
        catch(NamingException ex)
        {
            System.out.println("NamingException " + ex);
        }
        catch(Exception ex)
        {
            System.out.println("Exception " + ex);
        }
        // ende try - catch

    }  // end main
}

Mit dem Propertiesobjekt setzt man die Umgebung, die die JVM braucht. Hier deponiert man die notwendige Information, damit nach dem Aufbau der TCP/IP-Verbindung das Protokoll iiop darübergelegt werden kann und die Suche nach dem Objekt mit dem Namen "ejb/SayHelloJNDI" gestartet werden kann (lookup). Einstuegspunkt ist dann ein Objekt der Klasse SerialInitContextFactory. Von diesem werden zahlreiche weitere Objekte instanziiert um den reibungslosen Ablauf der Verbindung sicherzustellen.

Der Client sollte sich anstandslos kompilieren lassen. Allerdings erzeugt der Aufruf der Klasse zunächst einen Fehler:

about to create initialcontext
NamingException javax.naming.NoInitialContextException: Cannot instantiate class:
com.sun.enterprise.naming.SerialInitContextFactory [Root exception is java.lang.ClassNotFoundException:
com.sun.enterprise.naming.SerialInitContextFactory]

Offensichtlich fehlt die Klasse "com.sun.enterprise.naming.SerialInitContextFactory". Und sie ist nicht die einzige. Es fehlen noch eine ganze Reihe von Klassen. Sie befinden sich in drei Archiven, die beim Installieren von Glassfish im Verzeichnis "SDK/lib" abgelegt werden, also auf der Serverseite. Es handelt sich dabei um die folgenden drei Archive:

Hier sieht man den Preis, den man für die Vereinfachung der Entwicklung von Enterprise Java Beans 3.0 zahlt. Früher brauchte man für einen Client lediglich zwei Stubklassen, sowie das Remote- und das Home-Interface. Diese Information war für den Client ausreichend um sich über iiop mit einem Beanserver zu verbinden. Wir brauchen allerdings nicht alle Klassen dieser drei oben angegeben jars. Ich habe die notwendigen Klassen in ein eigenes Archiv zusammengefaßt. Sicher kann man dieses Archiv noch optimieren. Ich habe dieses Extrakt mit einigen Stateless und Stateful Session Beans getestet, kann aber nicht garantieren, daß es nicht doch Situationen gibt, in denen man mehr Klassen aus den drei oben erwähnten Packages braucht.

Das Konzentrat steht unter dem Namen "appserv-client.jar" zum Herunterladen bereit.   appserv-client.jar

Man sollte es wie schon bei "javaee.jar" in beide ext-Verzeichnisse legen. Die Java-Umgebung (JRE) verfügt unter /lib/ext über ein Verzeichnis für externe Java-Archive. Darüber hinaus enthält das JDK neben dem Compiler eine eigene Java-Umgebung (JRE) in der es auch ein Verzeichnis /lib/ext gibt.

Wird der Javainterpreter von der Konsole aufgerufen, so entstammt er der JRE und greift dementsprechend auf das ext-Verzeichnis der JRE zu. Auch die Entwicklungsumgebung Eclipse verwendet den Interpreter aus der JRE. Textpad hingegen benützt beim Ausführen der Javaprogramme den Interpreter der in JDK eingelagerten JRE.

Hinweis

Für Message-Driven-Beans sind zwei weitere Archive auf Clientseite notwendig. Man lese hierzu erst das Kapitel über JMS (Java Message Service).

Die Konsolausgabe des Client


Wie man sieht, ist der Verbindungsaufbau gelungen und die Bean hat uns eine Antwort geschickt.

System.out-Meldungen der Bean landen in der log-Datei server.log


System.out-Meldungen der Bean werden nicht auf die Serverkonsole ausgegeben sondern landen in der log-Datei:

[#|2010-07-08T11:14:20.937+0200|INFO|sun-appserver2.1|javax.enterprise.system.stream.out|_ThreadID=16;
_ThreadName=p: thread-pool-1; w: 7;|constructor SayHelloBean() called|#]

Dadurch kann man Fehler mit Hilfe der Datei server.log aufspüren. Man sieht hier auch, daß der Deployvorgang nicht automatisch das Erstellen eine Beanobjekts nach sich zieht. Erst die Clientanfrage führt zur Instanziierung des Beanobjekts.

top Back Next Up Home