Beispiel: Einfache Container Managed Entity Bean (JBoss 4.0)


Beispiel für eine Container Managed Entity Bean, auf die per Applicationclient zugegriffen wird.
Die Deployanleitung bezieht sich auf JBoss 4.0.3.
Hier gibt es das Projekt zum Download (dies ist ein EAR-Export, die Importanleitung findet man im Stateless-Beispiel): KuchenSingle.ear
ACHTUNG: Nach dem Import XDoclet-Builder im EJB-Projekt aktivieren !

Aufbau des Beispieles


a) Entity Bean-Klasse mit Remote-Interfaces.
b) Application Client


Anlegen der Application

Ein "EAR Application Project" mit dem Namen "KuchenSingle" erstellen.
Neue Anwendung (1)
Zu erzeugende Module definieren. Dieses Beispiel benötigt ein EJB-Projekt und ein Anwendungsclientprojekt.
Neue Anwendung (2)
Die J2EE-Hierarchie sollte so aussehen:
Erzeugte J2EE-Hierarchie


Anlegen der Entity Bean

Es wird eine neue Entity Bean angelegt. Dazu in der J2EE-Hierarchie "EJB Projects" -> "KuchenSingleEJB" -> "KuchenSingleEJB" -> "Entity Beans" wählen und im Contextmenü den Punkt "New..." -> "Other".
Eine neue Enterprise Java Bean zufügen.
Im ersten Schritt wird der Typ der Bean gewählt. ACHTUNG: Da es keine separate Option "Entity Bean" gibt fügen wir eine Session Bean zu !
Neue Entity Bean (1)
Das Package ist "com.knauf.ejb.kuchen", der Klassenname "KuchenSingleBean".
Neue Entity Bean (2)
Im nächsten Schritt belassen wir die Defaults, auch beim Bean-Typ ("State Type" ist natürlich für eine Entity-Bean falsch !).
Im folgenden Schritt entfernen wir das Interface "javax.ejb.SessionBean" und ersetzen es durch "javax.ejb.EntityBean".
Neue Entity Bean (3)
Nach dem Erzeugen ist die Datei "ejb-jar.xml" im EJB-Projekt leider fehlerhaft (das Element "<entity>" hatte kein öffnendes Tag ! Ein manuelles Einfügen hilft nicht da von XDoclet generiert. Aber nach dem nächsten Schritt ist das behoben.
Zuerst einmal löschen wir die gesammelten generierten Dateien in "EJB Projects" -> "KuchenSingleEJB" -> "KuchenSingleEJB" -> "ejbModule" -> "com.knauf.ejb.kuchen", so dass nur "KuchenSingleBean" übrig bleibt (alle anderen Dateien werden von XDoclet generiert). Siehe Screenshot.
Unnötige Klassen löschen

Wir ändern die XDoclet-Deklaration von "KuchenSingleBean":
Vom Plugin wurde dieses angelegt:
	 * @ejb.bean name="KuchenSingle"	
	 *           description="A session bean named KuchenSingle"
	 *           display-name="KuchenSingle"
	 *           jndi-name="KuchenSingle"
	 *           type="Stateless" 
	 *           transaction-type="Container" 
Wir ändern es in dieses ab:
	 * @ejb.bean name="KuchenSingle"	
	 *           description="Ein einzelner Kuchen"
	 *           display-name="KuchenSingle"
	 *           jndi-name="KuchenSingle"
	 *           type="CMP" 
	 *           transaction-type="Container"
	 *           cmp-version="2.x" 
	 *           view-type="remote" 
	 * @ejb.pk 
	 *           class="java.lang.Object" 
	 *           generate="false" 
	 * @jboss.unknown-pk 
	 *           class="java.lang.Integer" 
	 *           column-name="kuchenid" 
	 *           field-name="kuchenid" 
	 *           sql-type="INTEGER" 
	 *           jdbc-type="INTEGER" 
	 *           auto-increment="true" 
	 * @jboss.entity-command 
	 *           name="hsqldb-fetch-key" 
	 * @ejb.finder 
	 *           signature="java.util.Collection findAll()" 
	 *           query="select Object(o) from KuchenSingle o order by o.name"  
Die einzelnen Angaben bedeuteten dieses:


Eine static Logger-Variable wird erzeugt.
Anschließend implementieren wir die Methode "ejbCreate" sowie die zugehörige "ejbPostCreate":
	/**@ejb.create-method view-type="remote"
	 * @generated
	 */
	public Object ejbCreate () throws CreateException
	{
		KuchenSingleBean.logger.info("ejbCreate");
		return null;
	}

	
	/**Nach dem Erzeugen eines Kuchen-Objekts, Name ist noch nicht gegeben.
	 * @exception CreateException Fehler beim Erzeugen.
	 */
	public void ejbPostCreate () throws CreateException
	{
		KuchenSingleBean.logger.info("ejbPostCreate");
	} 
Jetzt noch ein weiteres persistentes Feld "Name" einbauen und wir haben eine lauffähige Entity Bean erzeugt.
	/**Der Name des Kuchens.
	 * @return
	 * @ejb.persistence column-name="name" sql-type="VARCHAR" 
	 * @ejb.interface-method view-type="remote" 
	 */
	public abstract String getName();

	/**Der Name des Kuchens.
	 * @param str_Name Der neue Name
	 * @ejb.persistence column-name="name" sql-type="VARCHAR" 
	 * @ejb.interface-method view-type="remote" 
	 */
	public abstract void setName (String str_Name); 
Wir geben hier den Spaltennamen ("kuchen") und den SQL-Typ an, nötig wäre beides nicht gewesen, das Tag "@ejb.persistence" alleine hätte ausgereicht da Daten des Feld ein einfacher Java-Typ ist.


Die J2EE-Hierarchie sieht jetzt so aus:
Erzeugte EJB-Dateien
"ejb-jar-xml" enthält die allgemeinen Bean-Definitionen:
	<entity>
	...
		<ejb-class>com.knauf.ejb.kuchen.KuchenSingleCMP</ejb-class>
	       	<persistence-type>Container</persistence-type>
	      	<prim-key-class>java.lang.Object</prim-key-class>
	       	<reentrant>false</reentrant>
	       	<cmp-version>2.x</cmp-version>
	       	<abstract-schema-name>KuchenSingle</abstract-schema-name>
	       	<cmp-field >
			<description><![CDATA[Der Name des Kuchens.]]></description>
			<field-name>name</field-name>
		</cmp-field>
		<query>
        		<query-method>
				<method-name>findAll</method-name>
				<method-params> </method-params>
			</query-method>
			<ejb-ql><![CDATA[select Object(o) from KuchenSingle o order by o.name]]></ejb-ql>
		</query>
	</entity> 
Hier taucht nur die Primary Key-Klasse als "java.lang.Object" auf, in den Feldern ("cmp-field") wird sie nicht erwähnt.

"jbosscmp-jdbc.xml" enthält JBoss-spezifische Angaben zur Bean.
	<entity>
		<ejb-name>KuchenSingle</ejb-name>
		<cmp-field>
			<field-name>name</field-name>
			<column-name>name</column-name>
		</cmp-field>
		<unknown-pk>
			<unknown-pk-class>java.lang.Integer</unknown-pk-class>
			<field-name>kuchenid</field-name>
			<column-name>kuchenid</column-name>
			<jdbc-type>INTEGER</jdbc-type>
			<sql-type>INTEGER</sql-type>
			<auto-increment/>
		</unknown-pk>
		<entity-command name="hsqldb-fetch-key"></entity-command>
	</entity> 
Hier taucht auch unsere Primary-Key-Definition wieder auf.


Jetzt können wir die Bean auf den Server stellen. Dieser Prozess erzeugt die Datenbanktabelle. Im Server-Log finden sich unter anderem diese Einträge, an der wir erkennen können wie die Tabelle erzeugt wird und mit welchem Statements ein Einfügen oder Löschen erfolgt:
org.jboss.ejb.plugins.cmp.jdbc.keygen.JDBCHsqldbCreateCommand.KuchenSingle] Insert Entity SQL: INSERT INTO KUCHENSINGLE (name) VALUES (?)
org.jboss.ejb.plugins.cmp.jdbc.JDBCCommandFactory.KuchenSingle] entity-command: [commandName=hsqldb-fetch-key,commandClass=class org.jboss.ejb.plugins.cmp.jdbc.keygen.JDBCHsqldbCreateCommand,attributes={}]
org.jboss.ejb.plugins.cmp.jdbc.JDBCRemoveEntityCommand.KuchenSingle] Remove SQL: DELETE FROM KUCHENSINGLE WHERE kuchenid=?
org.jboss.ejb.plugins.cmp.jdbc.JDBCStartCommand.KuchenSingle] Executing SQL: CREATE TABLE KUCHENSINGLE (kuchenid INTEGER NOT NULL IDENTITY, name VARCHAR(256), CONSTRAINT PK_KUCHENSINGLE PRIMARY KEY (kuchenid))
org.jboss.ejb.plugins.cmp.jdbc.JDBCFindByPrimaryKeyQuery.KuchenSingle#findByPrimaryKey] SQL: SELECT t0_KuchenSingle.kuchenid FROM KUCHENSINGLE t0_KuchenSingle WHERE t0_KuchenSingle.kuchenid=?
org.jboss.ejb.plugins.cmp.jdbc.JDBCQueryManager.KuchenSingle] Added findByPrimaryKey query command for home interface 


Datenbank-Adminstrations-Tool

Die Hypersonic-Datenbank bringt ein rudimentäres Administrationstool mit. Dieses erreichen wir, indem wir bei laufendem Server auf die JMX-Konsole gehen (http://localhost:8080/jmx-console) und in der Kategorie "jboss" die MBean "database=localDB,service=Hypersonic" auswählen.
Hypersonic Database Manager (1)
Ab zur Funktion "startDatabaseManager" und auf "Invoke" klicken.
Hypersonic Database Manager (2)
Es erscheint nur eine Seite mit einer schnöden Erfolgsmeldung, aber nach kurzer Zeit sollte sich eine Java-Anwendung, der "HSQL Database Manager" öffnen. Hier sehen wir auf der linken Seite die Tabelle unserer Bean.
Hypersonic Database Manager (3)


Application Client

Der Client muss die EJB-Jars referenzieren (siehe dazu Anleitung in den vorherigen Beispielen).
Wir bauen uns einen Client mit Swing-GUI. Dazu MUSS der Visual-Edit-Plugin installiert sein (siehe Installationsanleitung dieser Beispiele).
Wir wählen in der J2EE-Hierarchie "Application Client Projects" -> "KuchenSingleClient" -> "appClientModule" aus und gelangen per Rechtsklick -> "New" -> "Other..." in den Dialog zum Hinzufügen neuer Elemente. Wir wählen "Java" -> "Swing" (dieser Punkt ist nur vorhanden wenn Visual Edit installiert ist !) -> "JFrame Visual Class" aus.
Neuer Frame (1)
Package ist "com.knauf.ejb.kuchen", Klassenname ist "FrameKuchen". Erzeugte Kommentare können ebenfalls nicht schaden.
Neuer Frame (2)
Jetzt sollte der GUI-Designer des Visual Edit-Plugins starten. Wir legen JList auf einem JScrollPane und drei Buttons "Neu", "Bearbeiten" und "Löschen" an. Das Ergebnis sollte so aussehen:
FrameKuchen
Der Dialog "KuchenDialog" sollte so aussehen:
DialogKuchen

Die Main-Methode soll in einer Klasse "KuchenApplicationClient" liegen, die den FrameKuchen erzeugt und anzeigt.

Die EJB-Referenzen müssen im Deployment-Deskriptor des Clients deklariert werden. Leider kann uns XDoclet hier nicht mehr helfen, wir müssen also selbst Hand anlegen. Dazu die Datei "application-client.xml" öffnen und folgendes einfügen:
	<ejb-ref>
		<ejb-ref-name>ejb/KuchenSingle</ejb-ref-name>
		<ejb-ref-type>Session</ejb-ref-type>
		<home>com.knauf.ejb.kuchen.KuchenSingleHome</home>
		<remote>com.knauf.ejb.kuchen.KuchenSingle</remote>
		<ejb-link>KuchenSingle</ejb-link>
	</ejb-ref> 

Der Code für das JNDI-Lookup sieht so aus (im Konstruktor von "FrameKuchen"):
      Properties props = new Properties();
      props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
      props.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming.client");
      props.setProperty(Context.PROVIDER_URL, "jnp://localhost:1099");
      props.setProperty("j2ee.clientName", "KuchenSingleClient"); 

      InitialContext initialContext = new InitialContext(props);
      Object homeref = initialContext.lookup(KuchenSingleHome.COMP_NAME);
      this.kuchenHome = (KuchenSingleHome) PortableRemoteObject.narrow(homeref, KuchenSingleHome.class); 
Wichtig: die Property j2ee.clientName muss den Namen des Projekts haben !

Jetzt noch die Main class in "MANIFEST.MF" eintragen:
	Manifest-Version: 1.0
	Class-Path: KuchenSingleEJB.jar
	Main-Class: com.knauf.ejb.kuchen.KuchenApplicationClient


Ausführen des Clients

Im Menü "Run" den Punkt "Run..." wählen. In der Auswahl "Configurations" unter "Java Application" eine neue Konfiguration erstellen. Die Einstellungen sollten so aussehen:
Anwendungsclient ausführen
Zu beachten ist, dass vor der Anwendung der EJB-Server gestartet sein muss.


Version 1.0.1.0, Stand 27.11.2005
Historie:
1.0.0.0 (19.09.2005): Erstellt
1.0.0.1 (27.10.2005): EAR-Datei: Verweis auf zu aktivierenden XDoclet-Builder.
1.0.0.2 (01.11.2005): "KuchenFrame" in "FrameKuchen" umbenannt. Teile der Beschreibung und der Screenshots waren noch von der alten Version mit der Main-Methode in "KuchenFrame" statt "KuchenApplicationClient".
1.0.1.0 (27.11.2005): Der Application Client verwendet EJB-Referenzen und jboss-client.xml