Enterprise JavaBeans 2.x Enterprise JavaBeans 2.x

Werbung
Enterprise JavaBeans 2.x
Autor
Stephan Metzler
© 2008 Version 3.0
J2EE – EJB 2.x
Prolog
Dieses Skript vermittelt die Theorie von Enterprise JavaBeans als Applikationsframework zum
Erstellen von verteilten Applikationen.
Der Inhalt ist gegliedert in:
A : Java 2 EnterpriseEdition
- Theorieaspekt der verteilten Systeme und Übersicht über das J2EE Applikationsframework
B : Enterprise JavaBeans
- Betrachtungen der einzelnen EJB Ausprägungen
C : Best Practice
- Diskussion einiger J2EE Idioms und weiterführende Beispiele mit dem JBoss AS
D : EJB 3.0
- Ausblick auf die EJB 3.0 Spezifikation
E : Aufsetzen und Konfiguration der Entwicklungsumgebung
- Installationsanleitung für die begleitenden Beispiele
F : Lösungen zu den Übungen
- Lösungen zu den begleitenden Beispielen
G : Glossar
- ein Glossar mit den verwendeten Begriffen der EJB Thematik
H : Referenzen
- Literaturangaben, Online-Quellen sowie die verwendeten Software Versionen
© 2008 Stephan Metzler
Version 3.0
Seite 2 von total 218 Seiten
J2EE – EJB 2.x
Inhaltsverzeichnis
A
Java 2 EnterpriseEdition
8
1 Java Plattform
1.1 Verteilte Systeme
1.2
Technologien für Verteilte Systeme
8
8
9
2 J2EE Applikationsframework
2.1 Einsatzgebiete
2.2 Architektur
2.3 Namenskonventionen
2.4 Bestandteile einer J2EE-Anwendung
2.5 Multi-Tier Application
2.5.1
3 Schicht Architektur
2.5.2
J2EE Server mit WEB- und BusinessTier
2.5.3
J2EE Server mit JavaBean Komponenten als Interface
2.5.4
J2EE Server mit WEB-, BusinessTier und EIS Tier
B
Seite
Enterprise JavaBeans
10
10
10
11
11
12
12
12
12
12
13
3 EJB Definition
3.1 EJB Architektur
3.2 EJB Typen
3.3 Elemente einer EJB Komponente
3.3.1
Home Interface
3.3.2
Remote Interface
3.3.3
Bean Implementierung
3.3.4
Deployment Descriptor
13
14
14
15
15
15
15
15
4 Hello EJB World
4.1 Voraussetzungen
4.2 Aufgabe
4.3
Organisation
4.4 Umgebungsvariablen
4.5 Ziel
4.6 Hello EJB World Java Source Dateien
4.7 "Hello EJB World" Bean mit Sun Java™ Deploytool deployen
4.7.1
Applikations-Server starten / stoppen
4.7.2
Deployment Applikation starten
4.8 Deployment
4.9 Client Applikation ausführen
16
16
16
16
16
16
17
19
19
19
20
20
5 Inside EJB
5.1 EJB Klassen Diagramm
5.2 EJB Komponenten
5.2.1
HOME Interface
5.2.2
REMOTE Interface
5.2.3
BEAN Implementation
5.2.4
Deployment Descriptor
5.2.5
Überblick der Elemente einer EJB-Komponente
5.2.6
EJB Rollen:
5.3 Zugriff auf ein EJB
5.3.1
Sequenz Diagramm
24
24
25
25
26
26
26
27
27
28
29
© 2008 Stephan Metzler
Version 3.0
Seite 3 von total 218 Seiten
J2EE – EJB 2.x
5.4 RMI (Remote Method Invocation)
5.4.1
RMI Kommunikationsmodell
5.5 EJB Kommunikationsmodelle
5.5.1
synchrone, entfernte Kommunikation
5.5.2
synchrone, lokale Kommunikation
5.5.3
asynchron, nachrichtenbasierte Kommunikation
5.5.4
Überblick über die Kommunikationsmodelle
29
30
31
32
33
34
34
6 Stateless Session Bean ( zustandslos)
6.1 Bean Life Cycle
6.2 Übung ee1 : Zinsberechnung
35
35
36
7
Stateful Session Bean ( zustandsbehaftet )
Bean Life Cycle
7.2 Übung ee2 : Kontoführung
7.2.1
Konsolen Statements
7.2.2
Environment Entries als InitialContext
37
37
38
38
38
8 Entity Bean ( für die Abbildung von Daten einer DB )
8.1 Bean Life Cycle
8.1.1
EJB-Container Life-Cycle- Methoden
8.1.2
Persistenzoperationen einer Entity Bean
8.2 Modellieren von persistenten Geschäftsdaten
8.2.1
Übung ee3 : Personenverwaltung mit CMP 1.0
8.2.2
CMP/CMR in EJB 2.0
8.2.3
Übung ee31 : Triviale Kundenverwaltung mit Entity Bean (CMP 2.0)
39
39
40
40
41
41
45
45
7.1
C
Best Practice
9 Design Patterns
9.1 Einführung
9.1.1
Zweck
9.1.2
Geschichtliches
9.1.3
Referenzen
9.2 Singleton Pattern
9.2.1
Problem
9.2.2
Lösung
9.2.3
Implementation
9.3 Proxy Pattern
9.3.1
Problem
9.3.2
Lösung
9.3.3
Implementation
9.4 Factory Method Pattern
9.4.1
Problem
9.4.2
Lösung
9.4.3
Implementation
9.5 J2EE Pattern
9.5.1
Service Locator
9.5.2
Business Delegate
9.5.3
Value Object
9.5.4
Session Façade
9.6 Weitere Pattern im J2EE Umfeld
9.6.1
Value Object Assembler
9.6.2
Composite Entity
9.6.3
Data Access Objects DAO
© 2008 Stephan Metzler
50
50
50
50
50
50
51
51
51
51
53
53
53
54
55
55
55
56
57
58
60
61
63
64
64
64
65
Version 3.0
Seite 4 von total 218 Seiten
J2EE – EJB 2.x
9.6.4
10
Core J2EE Patterns
Programmiereinschränkungen (Regeln)
65
66
11
EJB Anwendungsbeispiele mit JBoss AS
11.1
wiederverwendbare Komponenten
11.2
EJB Container
11.3
EJB Ausprägungen
11.4
EJB Schnittstellen
11.5
EJB Bestandteile
11.6
EJB Implementation
11.7
EJB Deployment Descriptors
67
67
67
68
69
69
70
71
12
Übung ee4 : Datenanbindung mit Stateless Session Bean
JDBC API
12.2
Währungsrechner mit Session-Beans
12.2.1 Datenbankanbindung mit Session-Beans
12.2.2 JDBC Connection
12.2.3 JNDI lookup Connection
74
74
74
74
75
75
13
Übung ee5 : Robuste Referenzen auf Stateful Session Beans
13.1
Handles
13.2
Kontoführung mit Stateful Session Bean
13.2.1 Bean Identität mit Handle speichern
13.2.2 Handle persistent in Datei speichern
76
76
77
77
78
14
Übung ee6 : EJB QL (CMP 2.0)
14.1
EJB QL
14.2
Primary Key Klasse
14.3
ProductShop
14.3.1 Summe aus allen Produkten
79
79
81
82
83
15
Übung ee62 : PersonBean mittels BMP Entity Bean
15.1
BMP – Bean Managed Persistence
15.2
BMP Person Bean
84
84
85
16
Servlets
16.1
Vor- und Nachteile dynamischer Web-Inhalte
16.2
Grundlagen
16.3
Funktionsweise
16.4
Lebenszyklus
16.5
Übung ee7 : Servlet – ein Beispiel
16.6
Übung ee71 : Web Browser als EJB Client
89
89
89
89
90
91
93
17
JSP JavaServer Pages
17.1
Grundidee
17.2
Funktionsweise
17.3
Organisation
17.4
Aufbau von JavaServer Pages
17.5
Kommentare
17.6
Ausdrücke / Expressions
17.7
Direktiven
17.8
Deklarationen
17.9
Skriptlets
17.10 Aktionen
17.11 Fehlerbehandlung
17.12 Vordefinierte Variablen
17.13 Übung ee8: JSP Währungsrechner mit JDBC, TagLib und EJB
94
94
94
94
95
95
95
96
96
97
98
98
99
99
12.1
© 2008 Stephan Metzler
Version 3.0
Seite 5 von total 218 Seiten
J2EE – EJB 2.x
D
18
J2EE Security
18.1
EJB-Security
18.1.1 Security Roles
18.2
Client-Tier Sicherheit
18.2.1 ApplicationClients
18.2.2 WebClients
18.3
JNDI Sicherheit
18.4
Realms
18.5
Legacy Tier
18.6
Declarative vs Programmatic Security
18.7
J2EE Security Architecture
18.8
Role Mapping
18.9
ejb-jar.xml Security Elemente
18.10 Übung ee9 : J2EE Security
18.10.1 deklarative Security
18.10.2 prommatische Security
101
101
103
106
106
106
107
107
107
108
108
108
109
110
111
111
19
Transaktionen
19.1
Transaktionskonzepte
19.1.1 Lokale und verteilte Transaktionen
19.1.2 Zwei Phasen Commit
19.2
Java Transaction API (JTA) und Java Transaction Service (JTS)
19.3
User-Transaktionen
19.3.1 Bean Managed Transaction
19.3.2 Container-Managed-Transactions
19.3.3 JDBC Transaktionen
19.4
Übung ee10 : EJB Transaction
19.4.1 Transaktionssteuerung
112
112
112
113
113
115
115
117
119
120
120
20
Message Driven Beans
20.1
JMS
20.2
MDB
20.2.1 Bean Life Cycle
20.3
Übung ee11 : MDB Wetterstation
124
124
126
126
127
EJB 3.0
129
21
Alles wird viel einfacher!
21.1
EJB 3.0 Spezifikation
21.2
EJB 3.0 Features
21.3
Meta-Annotationen
21.4
JNDI Kapselung
21.5
Configuration by Exception
21.6
Callback-Annotationen für Lifecycle Events
21.7
Interzeptor-Methoden
21.8
Stateless Session Bean
21.9
Statefull Session Bean
21.10 Message Driven Bean
21.11 Entity Bean
21.12 EJB QL
21.13 Zusammenfassung
E
129
129
129
130
131
132
132
132
133
134
135
135
137
138
Aufsetzen und Konfiguration der Entwicklungsumgebung
© 2008 Stephan Metzler
139
Version 3.0
Seite 6 von total 218 Seiten
J2EE – EJB 2.x
22
Entwicklungstools für J2EE
139
23
Java J2SE / J2EE Installation
139
24
Eclipse als Entwicklungsumgebung
24.1
Installation
24.2
Eclipse starten
140
140
140
25
JBoss AS
25.1
Installation
25.2
JBoss AS starten / stoppen
25.3
JBoss Verzeichnisstruktur
25.4
Benutzerspezifischer Server einrichten
25.5
Benutzerspezifischer JBoss-Server starten
141
141
141
142
142
143
mySQL Datenbankserver installieren und Datenbank erstellen
26.1
Installation
26.2
mySQL-Tools
26.3
EnterpriseEducation DB anlegen
144
144
144
144
27
145
26
EJB Applikation mit Eclipse / JBoss
28
Tomcat
28.1
Server Architektur
28.2
Installation
F
149
149
150
Lösungen zu den Übungen
151
29
Lösungen zu Übungen aus dem Skript
29.1
Lösung zu Übung ee1 : Stateless Session Bean – Zinsberechnung
29.2
Lösung zu Übung ee2 : Stateful Session Bean – Kontoführung
29.3
Lösung zu Übung ee3 : CMP 1.0 Entity Bean – Personenverwaltung
29.4
Lösung zu Übung ee3.1 : Entity Bean CMP 2.0 - Kundenverwaltung
29.5
Lösung zu Übung ee4 : DB Session Bean - Money Exchange
29.6
Lösung zu Übung ee5 : Handle auf Session Bean - AutomaticTeller
29.7
Lösung zu Übung ee6 : CMP 2.0 Entity Bean - ProductShop
29.8
Lösung zu Übung ee61 : CMP2.0 Local Interface - Summe aller Produkte
29.9
Lösung zu Übung ee62: BMP Entity Bean - PersonBean
29.10 Lösung zu Übung ee7 : Servlet - WorkingServlet
29.11 Lösung zu Übung ee71: Servlet mit EJB Client - Web Shop
29.12 Lösung zu Übung ee8 : JSP mit JDBC, Taglib und EJB - Währungsrechner
29.13 Lösung zu Übung ee91 : EJB Security
29.14 Lösung zu Übung ee10 : EJB Transaction
29.15 Lösung zu Übung ee11 : MDB - WetterTopic
151
151
153
156
160
165
169
173
177
182
187
189
192
197
201
207
G
Glossar
209
H
Referenzen
216
30
Quellenangaben
30.1
Bücher
30.2
Online Quellen
216
216
217
31
218
Installierte Software
© 2008 Stephan Metzler
Version 3.0
Seite 7 von total 218 Seiten
J2EE – EJB 2.x
A
Java 2 EnterpriseEdition
1
Java Plattform
Die Java Plattform ist in drei Editionen aufgeteilt:
J2ME
Micro Edition
J2ME definiert eine robuste,
flexible Umgebung für
Applikationen in:
consumer devices
wie Mobil-Telefone, PDAs
- embedded devices
J2ME beinhaltet eine VM und
Standart Java APIs für
Kommunikationsprozesse.
-
1.1
J2EE
J2SE
Standard Edition
Enterprise Edition
Die J2SE definiert zwei Produkte:
-
JRE, Runtime Environment
beinhaltet APIs, VM, etc.
JDK, Development Kit
beinhaltet JRE sowie Compiler,
Debugger, etc.
Stellt die Basis für J2EE dar.
J2EE definiert einen Standart
um Multi-Tier Enterprise
Applikationen zu entwickeln.
Stellt verschiedene Dienstleistungen den einzelnen
Komponenten zur Verfügung
und bietet transparente
Lösungen für dessen
vielseitiges Verhalten.
Verteilte Systeme
Ein verteiltes System besteht aus mehreren Prozessen, die mittels Nachrichtenaustausch
miteinander kommunizieren. Dabei sind wichtige Kriterien:
Gemeinsame Nutzung von Ressourcen
- Zugriff, Verzögerung und Updates müssen zuverlässig und
konsistent sein
Offenheit
-
Erweiterbarkeit des Systems (Hardware, Software)
Standardisierung von Schnittstellen erforderlich
Interprozesskommunikation muss unterstützt werden
heterogene HW- und SW- Komponenten sind Bestandteil eines
offenen Systems
Client Server Architektur
Skalierbarkeit
- Skalierung des Systems ohne Änderungen der System- oder
Anwendungssoftware
Fehlertoleranz
- Hardwareredundanz, Softwareredundanz
Peer-to-Peer Architektuer
© 2008 Stephan Metzler
Java Plattform
Version 3.0
Seite 8 von total 218 Seiten
J2EE – EJB 2.x
1.2
Technologien für Verteilte Systeme
CORBA
(Common Object Request Broker Architecture):
- Plattform- und Sprachunabhängig
- nur "basic distribution"
DCOM
(Distributed COM) Dienste zur Kommunikation
zwischen Prozessen
- fokussiert auf Microsoft
- CORBA-DCOM Bridge
.NET
(Microsoft)
- Sprach- und (Plattform-) unabhängiges
Framework für Verteilte Systeme
EJB
(Enterprise Java Beans)
- nur JAVA
- entwickelt für Geschäftsprozesse
- Sicherheits- und Verbindungslogik ist integriert
© 2008 Stephan Metzler
Java Plattform
Version 3.0
Seite 9 von total 218 Seiten
J2EE – EJB 2.x
2
J2EE Applikationsframework
J2EE bezeichnet ein Applikationsframework, ausgelegt auf:
- Performance
- Skalierbarkeit
- Plattformunabhängigkeit
Die Trennung zwischen Client-Tier, Business-Tier und Data-Tier ist sehr sauber implementiert
und erlaubt eine weitgehende Unabhängigkeit von den verwendeten Clients und den Systemen
zur Datenhaltung im Backend.
J2EE Applikations-Server beinhaltet:
- Components
Applets, Servlets, Enterprise Java Beans
- Containers
Web Browsers, Servlet Engines, EJB Containers
- Resources
SQL Datenquellen, Nachrichtensysteme, Mailsysteme
2.1
Einsatzgebiete
J2EE Applikationen finden in den folgenden Gebieten ihren Schwerpunkt:
-
2.2
Datenbankzugriff
Dynamische Internetseiten
Enterprise JavaBeans
Middle-Tier Server
Plattformenabhängige Kommunikation
Sicherheit
Trennung der Anwenderschwerpunkte
Verteilte Systeme
Web-Server-Einsatz
Architektur
Funktionelle Einheiten werden als Komponenten bezeichnet und zu einer KomponentenArchitektur zusammengefasst. Dabei dominieren die folgenden "Core Technologies":
- JNDI (Java Naming and Directory Interface)
ermöglicht den Zugriff auf "naming" und "directory" Systeme wie LDAP, COS oder DNS
- JTA (Java Transaktion API)
ermöglicht den Zugriff auf Transaction Systeme wie BEA Tuxedo, etc.
- JDBC (Java DataBase Connectivity)
ermöglicht Zugriff zu relationalen Datenbanken
ORACLE
DB2
- JMS (Java Messaging Service )
ermöglicht den Zugriff zu Message
Orientated
Middleware, z.B. iBus ,IBM MQSeries
JDBC
BEATuxedo
JTA
J2EE
LDAP
JNDI
DNS
COS
JMS
IBM MQSeries
iBus
© 2008 Stephan Metzler
J2EE Applikationsframework
Version 3.0
Seite 10 von total 218 Seiten
J2EE – EJB 2.x
2.3
2.4
Namenskonventionen
Item
Syntax
Beispiel
Enterprise Bean Name (DD)
<name>Bean
HelloWorldBean
EJB JAR Display Name (DD)
<name>JAR
HelloWorldJAR
Enterprise Bean Class
<name>Bean
HelloWorldBean
Home Interface
<name>Home
HelloWorldHome
Remote Interface
<name>
HelloWorld
Local Home Interface
<name>LocalHome
HelloWorldLocalHome
Local Remote Interface
<name>Local
HelloWorldLocal
Abstract Schema (DD)
<name>
HelloWorld
Bestandteile einer J2EE-Anwendung
Eine J2EE-Anwendung ist eine J2EE-Komponente, die über einen J2EE-Server verschiedenen
Clients zur Verfügung gestellt wird. Die Bestandteile einer J2EE-Anwendung werden in eine
*.ear Datei (e
enterprise archive)
gepackt und können aus den folgenden Komponenten
ar
bestehen:
- Enterprise Beans
eine oder mehrere Enterprise Beans in einem
*.jar Datei (java archive)
- WEBWEB-Anwendungen
HTML-Seiten, Servlets, JSP, gif- und jpg-Bilder
in einer *.war Datei (web application archive)
- ClientClient-Anwendungen
Stand-Alone Java Applikation in einer
*.jar Datei (java archive)
HelloWorld.ear
HelloWorld.jar
HelloWorldClient.jar
HelloWorldWeb.war
- Deployment Descriptors
XML-Dokummente zur Integrationsbeschreibung
(sind Bestandteil jeder EAR Komponente)
Das Enterprise Archive folgt nebenstehender
Struktur die dem Applikationsserver das Entpacken
der Komponenten und das Deployen der J2EE
Applikation ermöglicht:
© 2008 Stephan Metzler
J2EE Applikationsframework
Version 3.0
Seite 11 von total 218 Seiten
J2EE – EJB 2.x
2.5
Multi-Tier Application
Das Verteilen einer Applikation richtet sich meistens nach dem "seperation of concern" Aspekt
der OOA.
2.5.1
3 Schicht Architektur
RMI = Remote Method Invocation
IIOP = Internet Inter ORB Protocol
WEB Browser
HTML, Applets
HTTP
WEB Server
Java Servlets
JSP
J2EE Client Container
J2EE Client
stand alone Java Client
DATA
RMIRMI-IIOP
Applikation Server
EJB
C++ Client
IIOP
CLIENT-Tier
LOGIK-Tier
2.5.2
J2EE Server mit WEB- und BusinessTier
2.5.3
J2EE Server mit JavaBean Komponenten als Interface
2.5.4
J2EE Server mit WEB-, BusinessTier und EIS Tier
© 2008 Stephan Metzler
J2EE Applikationsframework
DATA-Tier
Version 3.0
Seite 12 von total 218 Seiten
J2EE – EJB 2.x
B
Enterprise JavaBeans
3
EJB Definition
Development,
Development
Distribution
and
Deployment
of
JavaJava-Based
Based
ServerServer-Side
Software Components
Entwicklung,
Entwicklung
Verteilung
und
Umsetzung (von Modellen)
Militär: Truppenbereitstellung / Installation
Java basierten
serverseitigen
Software Komponenten
EJB Technologie definiert
- Interfaces
- Patterns
- Richtlinien
für komponenten-orientierte, verteilte Systeme.
EJB Komponente
-
definiert Funktionalität
Black Box
Zugriff nur über Schnittstelle
Wiederverwendbarkeit
EJB Ziele
- Entwicklung von Geschäftsprozessen ( keine Systemprozesse )
- Standard für Komponenten basierte Entwicklung
- Wiederverwendbarkeit
http://www.ejip.net/images/ejb.jpg
© 2008 Stephan Metzler
EJB Definition
Version 3.0
Seite 13 von total 218 Seiten
J2EE – EJB 2.x
3.1
EJB Architektur
- EJBs existieren innerhalb von Containern
- Applikations-Server stellt Dienste zur Verfügung
EJB
Client
EJB
Applikation
Server
EJB
EJB CONTAINER
Client
Services
Client
Transaction
Security
Persistance
3.2
Distribution
Naming
EJB Typen
Enterprise-Bean
interface javax.ejb.EnterpriseBean
Session Bean
Entity Bean
Message Driven Bean
interface javax.ejb.SessionBean
interface javax.ejb.EntityBean
interface javax.ejb.MessageDrivenBean
Session Bean
Session Bean
Entity Bean
Entity Bean
stateless
statefull
Container Managed Pesistence
Bean Managed Pesistence
Zweck
Session Bean
Entity Bean
Message Driven Bean
Stellt einen Prozess für einen
Client dar, z.B. Geldautomat
Stellt Daten dar. Entitäten oder
Objekte im persistenten
Speicher (DB)
Stellt einen Nachrichtenempfänger dar.
Stateless Session Bean
Bean Managed Persistence
Statefull SessionBean
Container Managed Persistence
Ausprägung
Gemeinsame
Nutzung
Persistenz
© 2008 Stephan Metzler
EJB Definition
Message Bean
Message Driven Bean
Statefull Session Bean
dedizierte Client Zuordnung
Stateless Session Bean hat
keine Client Zuordnung
Transient
Lebenszyklus typischerweise
durch Clientprozess bestimmt
Gemeinsame Nutzung durch
mehrere Clients möglich
keine direkte Clientzuordnung, "horcht" auf
einem JMs-Kanal
Persistent
Transient
Zustand bleibt im persistenten
"stirbt" bei Container
Speicher (DB) auch nach
Terminisierung
Container Terminierung erhalten
Version 3.0
Seite 14 von total 218 Seiten
J2EE – EJB 2.x
3.3
Elemente einer EJB Komponente
Die EJB besteht aus mindestens vier Komponenten:
3.3.1
Home Interface
- definiert Schnittstelle (Sichtbarkeit)
was sieht der Client von dieser Komponente
Client
Datei: ejb/HelloWorldHome.class
3.3.2
Remote Interface
- definiert Dienste (Business Methoden),
die dem Client zur Verfügung stehen
Datei: ejb/HelloWorld.class
3.3.3
Bean Implementierung
- definiert und implementiert alle Methoden,
die im Home- und Remote Interface
deklariert sind
Home
Interface
Remote
Interface
EJB
Implementation
EJB
Container
Deployment
Descriptor
Datei: ejb/HelloWorldBean.class
3.3.4
Deployment Descriptor
- XML Datei zur Beschreibung der Komponente
(Dateiangaben, Pfadangaben, Datenbankverbindungen und das Verhalten, z.B:
Zugriffskontrolle, Transaktionsverhalten)
Datei: ejb-jar.xml
HelloWorld.jar
© 2008 Stephan Metzler
EJB Definition
Java ARchive
AR
(Datei mit der Endung .jar)
ejb / HelloWorld.class
Remote Interface
ejb / HelloWorldHome.class
Home Interface
ejb / HelloWorldBean.class
Bean Implementation
ejb-jar.xml
Deployment Descriptor
Version 3.0
Seite 15 von total 218 Seiten
J2EE – EJB 2.x
4
Hello EJB World
4.1
Voraussetzungen
- J2SDK: Java Software Development Kit
- J2EE: Java Application Server
(<INSTALL_DIR>\lib\j2ee.jar als Library für die Entwicklungsumgebung)
- Entwicklingsumgebung: Eclipse, Java Editor Tool, etc
4.2
Aufgabe
Aufbau, Installieren und Betreiben einer J2EE Applikation
4.3
Organisation
/bin
generierte Class Dateien
/deployment
generierte WAR Dateien
generierte JAR Dateien
Bibliotheks-Klassen
/src/client
Source Dateien der
Client Applikation
/src/ejb
4.4
Source Dateien der
Middle Tier Applikation
(EJBs, Servlets und HelperClasses)
Umgebungsvariablen
Benutzer- oder Systemvariablen definieren/ergänzen:
4.5
Ziel
1.
Sun Java™ System
Application Server Plattform
2. HelloWorld.jar
3. deployen
4. HelloWorldClient.class
© 2008 Stephan Metzler
Hello EJB World
Version 3.0
Seite 16 von total 218 Seiten
J2EE – EJB 2.x
4.6
Hello EJB World Java Source Dateien
- das Home Interface
- im Verzeichnis
HelloWorldHome.java
...\ee0\src\ejb
package ejb;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface HelloWorldHome extends EJBHome {
HelloWorld create() throws RemoteException, CreateException;
}
- das Remote Interface
- im Verzeichnis
HelloWorld.java
...\ee0\src\ejb
package ejb;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface HelloWorld extends EJBObject {
public String sayHello() throws RemoteException;
}
- die Enterprise BEAN Klasse
- im Verzeichnis
HelloWorldBean.java
...\ee0\src\ejb
package ejb;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
public class HelloWorldBean implements SessionBean {
public String sayHello() {
return "Hello EJB World";
}
public void ejbCreate() {}
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext sc) {}
}
© 2008 Stephan Metzler
Hello EJB World
Version 3.0
Seite 17 von total 218 Seiten
J2EE – EJB 2.x
- die Client Applikations-Klasse
- im Verzeichnis
HelloWorldClient.java
...\ee0\src\client
package client;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import ejb.HelloWorld;
import ejb.HelloWorldHome;
/* VM Arguments:
*
*
-Dorg.omg.CORBA.ORBInitalHost=$localshost
*
-Dorg.omg.CORBA.ORBInitalPort=$3700
*/
/* System Properties:
*
*
System.setProperty("org.omg.CORBA.ORBInitalHost","localhost");
*
System.setProperty("org.omg.CORBA.ORBInitalPort","3700");
*/
/* Environment Properties:
*
*
Properties env = new Properties();
*
env.put(Context.INITIAL_CONTEXT_FACTORY,
*
"com.sun.jndi.cosnaming.CNCtxFactory");
*
env.put(Context.PROVIDER_URL, "iiop://localhost:3700");
*/
public class HelloWorldClient {
public static void main(String[] args) {
try {
Context initial = new InitialContext();
Object objref = initial.lookup("HelloWorldBean");
HelloWorldHome home = (HelloWorldHome)
PortableRemoteObject.narrow(
objref, HelloWorldHome.class);
HelloWorld helloWorld = home.create();
System.out.println(helloWorld.sayHello());
} catch (Exception ex) {
System.err.println("unexpected exception!");
ex.printStackTrace();
}
}
}
© 2008 Stephan Metzler
Hello EJB World
Version 3.0
Seite 18 von total 218 Seiten
J2EE – EJB 2.x
4.7
"Hello EJB World" Bean mit Sun Java™ Deploytool deployen
Mit dem Sun™ Deploytool die notwendigen Deployment Descriptoren menügeführt erstellen
und die EJB Komponenten im Sun AS™ deployen.
4.7.1
Applikations-Server starten / stoppen
- Start > Sun Microsystems > Application Server PE > Start / [Stop] Default Server
- Testen ob der Server läuft mit: http://localhost:8080
4.7.2
Deployment Applikation starten
- Start > Sun Microsystems > Application Server PE > Deploytool
JAR Datei definieren ( Packaging the Application ):
- File > New > Application …
- Application File Name
- Application Display Name
…\ee0\deployment\HelloWorld.ear
HelloWorld
- File > New > Enterprise Bean …
- JAR Name
- Edit Contents …
- Bean Klasse
- Home Interface
- Remote Interface
- Enterprise Bean Class
- Enterprise Bean Type
- [Remote] Home Interface
- [Remote] Remote Interface
HelloWorldJar
…\ee0\bin\ejb\HelloWorldBean.class
…\ee0\bin\ejb\HelloWorldHome.class
…\ee0\bin\ejb\HelloWorld.class
ejb/HelloWorldBean
Stateless Session
ejb/HelloWorldHome
ejb/HelloWorld
J2EE Applikation "deployen":
- Tools > Deploy ...
- Return Client JAR
…\ee0\deployment
# Archive HelloWorld.ear
META-INF\sun-j2ee-ri.project
META-INF\application.xml
META-INF\sun-application.xml
META-INF\MANIFEST.MF
ejb-jar-ic.jar
ejb-jar-ic.jar\ejb\HelloWorld.class
ejb-jar-ic.jar\ejb\HelloWorldHome.class
ejb-jar-ic.jar\ejb\HelloWorldBean.class
ejb-jar-ic.jar\META-INF\sun-j2ee-ri.project
ejb-jar-ic.jar\META-INF\ejb-jar.xml
ejb-jar-ic.jar\META-INF\sun-ejb-jar.xml
ejb-jar-ic.jar\META-INF\MANIFEST.MF
#
© 2008 Stephan Metzler
Hello EJB World
Version 3.0
Seite 19 von total 218 Seiten
J2EE – EJB 2.x
4.8
Deployment
Das Deployment definiert das serverseitige Bereitstellen der J2EE Komponenten. Dazu werden
neben den Javakomponenten die Deployment Descriptoren ejb-jar.xml (und weitere) erstellt
und gemeinsam in einer EAR Datei dem Applikationsserver übergeben.
Alternativen zum Bereitstellen dieser Komponenten mittels Deploytool sind:
- manuelles Bereitstellen
JAR Datei erstellen und dem Applikationsserver übergeben
(Deployment Descriptoren müssen vorhanden und in Jar Datei verpackt sein!)
copy
HelloWorld.jar
<J2EE_HOME> \domains\domain1\autodeploy
- ANT Script
mit dem Skript Tool von Jacarta Ant™ ( http://ant.apache.org ) das Packen der Applikation
und das Deployen der Komponenten durchführen:
z.B: asant deploy
asadmin deploy
4.9
--user <user-name>
--password <password>
--host <hostname>
--port <admin-port>
HelloWorld.jar
Client Applikation ausführen
mit Eclipse
Java Build Path (Eclipse)
HelloWorldClient.jar HelloWorldClient.jar
enthält Stub Klassen
enthält keine Stub Klassen
j2ee.jar
appserv-rt.jar
generierte Client JAR Datei
HelloWorldClient.jar
- ohne Stub Klassen
© 2008 Stephan Metzler
Hello EJB World
Version 3.0
Seite 20 von total 218 Seiten
J2EE – EJB 2.x
- Stubklassen generieren
HelloWorldClient.jar
# Archive HelloWorldClient.jar
ejb\_HelloWorld_Stub.class
ejb\_HelloWorldHome_Stub.class
ejb\HelloWorld.class
ejb\HelloWorldBean.class
ejb\HelloWorldHome.class
META-INF\sun-j2ee-ri.project
META-INF\MANIFEST.MF
META-INF\application.xml
META-INF\sun-application.xml
ejb-jar-ic.jar
ejb-jar-ic.jar\META-INF\ejb-jar.xml
ejb-jar-ic.jar\META-INF\sun-ejb-jar.xml
ejb-jar-ic.jar\META-INF\MANIFEST.MF
#
© 2008 Stephan Metzler
Hello EJB World
Version 3.0
Seite 21 von total 218 Seiten
J2EE – EJB 2.x
Konsole [cmd]:
Das folgende Batch Programm führt die "Hello EJB World" HelloWorldClient.class Applikation
ohne Entwicklungsumgebung aus:
ohne Stub-Files und ohne Environment Properties:
- cd …\ee0\bin
- java
wechselt zum Projekt \bin Verzeichnis
–cp
<J2EE_HOME>\lib\j2ee.jar;
<J2EE_HOME>\lib\appserv-rt.jar;
.
client.HelloWorldClient
CLASSPATH Settings
J2EE Library
für RMI-IIOP
aktuelles Verzeichnis
Client Applikation
mit Stub-Files und mit Environment Properties:
- cd …\ee0\bin
wechselt zum Projekt \bin Verzeichnis
- java –cp
CLASSPATH Settings
<J2EE_HOME>\lib\j2ee.jar;
J2EE Library
<J2EE_HOME>\lib\appserv-rt.jar;
für RMI-IIOP
..\deployment\HelloWorldClient.jar;
generiertes ClientJar
.
aktuelles Verzeichnis
client.HelloWorldClient
Client Applikation
ApplicationClient (HelloWorldAppClient.jar) mit Deploytool erstellen und ausführen mit:
- appclient -client HelloWorldAppClient.jar
im Verzeichnis
wo sich das JAR befindet
HelloWorldAppClient.jar:
# Archive HelloWorldAppClient.jar
client\HelloWorldClient.class
ejb\HelloWorld.class
ejb\HelloWorldHome.class
META-INF\sun-j2ee-ri.project
META-INF\application-client.xml
META-INF\sun-application-client.xml
META-INF\MANIFEST.MF
#
© 2008 Stephan Metzler
Hello EJB World
Version 3.0
Seite 22 von total 218 Seiten
J2EE – EJB 2.x
HelloWorldAppClient generieren:
© 2008 Stephan Metzler
Hello EJB World
Version 3.0
Seite 23 von total 218 Seiten
J2EE – EJB 2.x
5
Inside EJB
5.1
EJB Klassen Diagramm
rmi = Remote Method Invocation
Interfaces of:
java.ejb
java.rmi.Remote
javax.ejb.EnterpriseBean
package
javax.ejb.
- SessionBean
- EntityBean
java.ejb.EJBObject
getEJBHome()
getHandle()
getPrimaryKey()
isIdentical()
remove()
javax.ejb.EJBHome
getEJBMetaData()
getHomeHandle()
remove()
getEJBHome()
getHomeInterfaceClass()
getPrimaryKeyClass()
getRemoteInterfaceClass()
isSession()
isStatelessSession()
Remote Interface
Home Interface
HelloWorld
HelloWorldHome
business
methods
javax.ejb.EJBMetaData
<enterprise bean>
HelloWorldBean
create()
findByPrimaryKey()
findx()
Beans Provider's Classes
<guard>
EJBObject
<factory>
EJBHome
<context>
real context object
Container generated Classes
Runtime Classes
javax.ejb.EJBContext
javax.ejb. EntityContext
getEJBObject()
getEJBLocalObject()
getPrimaryKey()
javax.ejb. SessionContext
getEJBObject()
getEJBLocalObject
© 2008 Stephan Metzler
Inside EJB
getEJBHome()
getEJBLocalHome()
getCallerPrincipal()
isCallerinRole()
setRollbackOnly()
getRollbackOnly()
getUserTransaction()
Version 3.0
Seite 24 von total 218 Seiten
J2EE – EJB 2.x
5.2
EJB Komponenten
5.2.1
HOME Interface
Das HOME Interface definiert die Methoden der BEAN Komponenten zum:
-
Erstellen
Finden
Löschen
Zugreifen auf META Informationen - (Remote Interface Class Name, etc.)
Die Implementierung erfolgt durch die Implementation der "FACTORY Design Pattern" :
FACTORY Design Pattern:
Erzeugen einer neuen Instanz durch einen Methodenaufruf
Methodenaufruf eines
Factory Objektes anstelle der Verwendung des "new"
"new"
Konstruktors.
- Client, der das Bean erzeugt, hat keine direkte Referenz zur implementierten Klasse,
sondern die Referenz eines Proxy Objektes (EJBObject)
- Der EJB Kontainer kann auch eine bereits bestehende Instanz einer EJB Komponente
zurückgeben (pooling), anstatt eine neue Instanz zu bilden
- Das Erzeugen von Instanzen der EJB Komponenten ist vom EJB Container kontrolliert und
nicht vom Client (Clients haben verschiedene Zugriffsrechte)
Klassendiagramm des EJB HOME Interface
java.rmi.Remote
beschreibt die Netzwerkfähigkeit eines Objektes
javax.ejb.EJBHome
getEJBMetaData()
getHomeHandle()
remove()
erzeugt eine Instanz von EJBMetaData
erzeugt eine dauerhafte Instanz für einen späteren Zugriff
entfernt (löscht) das Enterprise Java Bean
HelloWorldHome
create()
findByPrimaryKey()
findXXX()
© 2008 Stephan Metzler
Inside EJB
erzeugt ein EJB (mehrere create Methoden sind möglich)
nur für ENTITY Beans
mehrere finder Methoden sind möglich
Version 3.0
Seite 25 von total 218 Seiten
J2EE – EJB 2.x
5.2.2
REMOTE Interface
Das REMOTE Interface erlaubt dem Client den Zugriff auf:
- die Business Methoden
- das Home Interface (und dessen Methoden)
Implementierung erfolgt durch die Implementation der "GUARD Design Pattern"
GUARD Design Pattern: Alle Methodenaufrufe eines Client werden auf die entsprechenden
Zugriffsrechte überprüft.
Klassendiagramm des EJB REMOTE Interface
java.rmi.Remote
beschreibt die Netzwerkfähigkeit eines Objektes
javax.ejb.EJBObject
getEJBHome()
getHandle()
remove()
getPrimaryKey()
isIdentical()
erzeugt eine Referenz zur Instanz des HOME Interface
robuste Referenz zur Netzwerkfähigkeit der Instanz
löscht eine EJB Instanz
gibt den PrimaryKey des EJB (throws RemoteException)
vergleicht zwei REMOTE Interfaces
HelloWorld
Business Methods
5.2.3
beschreibt den Geschäftsprozess
(Business Methoden)
BEAN Implementation
Stellt die Funktionalität des Bean zur Verfügung, d.h. eine Java Klasse (das HOME- und
REMOTE Interface sind "nur" Java Interfaces.)
- Business Methoden des REMOTE Interface
- Implementation der "Methoden des HOME Interface"
Kein Client und auch keine andere Bean kann direkt auf dieses Objekt zugreifen.
5.2.4
Deployment Descriptor
Der Deployment Descriptor ist eine XML (eX
Xtensible Markup Language) Datei und beschreibt
die Deploymenteigenschaften. Der Deployment Descriptor beeinflusst verschiedene
Entwicklungslevel (Rollen).
© 2008 Stephan Metzler
Inside EJB
Version 3.0
Seite 26 von total 218 Seiten
J2EE – EJB 2.x
Deployment Descriptor
Descriptor Beschreibungskriterien (Ausschnitt):
wie das Bean installiert werden muss
das Deployment (Installieren / Bereitstellen)
die EJB Implementation / das HOME Interface / das REMOTE Interface
Verbindungen zu anderen Quellen (EJB, DB, ...)
die Umgebung des Beans (damit ist das Bean nicht an eine bestimmte Umgebung
gebunden, lediglich die Konfiguration muss angepasst werden für eine andere Umgebung)
- Transaktionsvariablen (Transactions Attributes)
- Sicherheitsvariablen (Access Control Lists)
-
5.2.5
Überblick der Elemente einer EJB-Komponente
Die folgende Tabelle stellt alle möglichen Elemente einer EJB-Komponente im Überblick dar
und zeigt welche Elemente bei welchen EJB-Typen zur Anwendung kommen.
Session Bean
Element
Remote Home Interface
Local Home Interface
Remote Interface
Local Interface
Bean Klasse
Primary Key Klasse
Deployment Descriptor
5.2.6
local
Entity Bean
remote
local
remote
Message
Driven
Bean
EJB Rollen:
Durch die Trennung der verschiedenen Schichten ergibt sich neben der daraus resultierenden
Robustheit noch die Möglichkeit des parallelen Entwicklungsprozesses. Dieser Aufgabenteilung
sind die folgenden Rollen zugeordnet:
- Enterprise Bean Provider (beschreibt die
verwendeten Ressourcen)
- Service Provider (Hersteller eines J2EE
Servers, z.B. Sun, BEA, IBM, Borland, etc.)
- Container Provider (stellt die
Laufzeitumgebung für EJBs zur Verfügung)
- Application Assembler (verbindet die EJBs
und Datenquellen untereinander)
- Deployer (installiert die Applikation in den
Kontainer)
- Systemadministrator (administriert
Gesamtsystem, OS, J2EE-Server, etc.)
http://www.javaworld.com/javaworld/jw-07-1998/step/ejb-roles.gif
In der Praxis kann eine Person mehrere oder sogar alle Rollen übernehmen. Bei grösseren
Projekten werden die Rollen jedoch häufig auf Teams aufgeteilt.
© 2008 Stephan Metzler
Inside EJB
Version 3.0
Seite 27 von total 218 Seiten
J2EE – EJB 2.x
5.3
Zugriff auf ein EJB
Ein EJB erlaubt den Remote Zugriff nur über sein HOME Interface:
Der Client ermittelt zunächst per JNDI eine Referenz auf die Bean, welche unter dem Namen
HelloWorldBean im Verzeichnisdienst (JNDI) registriert ist.
// Connection to JNDI Services
InitialContext ic = new (InitialContext(Protokollparameter));
// Lookup Home Interface
Object obj = ic.lookup("HelloWorldBean");
Die erhaltene generische Referenz wird durch Aufruf der statischen Methode narrow der
Klasse PortableRemoteObject typsicher in eine Ausprägung der Home-Schnittstelle
(HelloWorldHome)konvertiert.
// Typecasting due to RMI over IIOP
HelloWorldHome home = (HelloWorldHome)PortableRemoteObject.narrow(
obj,HelloWorldHome.class);
Der Aufruf der in dieser Schnittstelle durch den Anwender definierten create-Methode sorgt
für die serverseitige Instanziierung der EJB, die als Ausprägung der Remote-Schnittstelle
geliefert wird. Tatsächlich wird nicht das EJB-Objekt selbst durch den Methodenaufruf
retourniert, sondern lediglich ein netzwerktransparenter Verweis darauf, der jedoch clientseitig
einer lokalen Objektreferenz gleichgestellt verwendet werden kann.
Ferner wird serverseitig zur Kommunikation mit der EJB ein Home-Objekt erzeugt, welchem
eine Stellvertreterrolle für den anfragenden Client zukommt. Der Aufruf der durch die EJB zur
Verfügung gestellten Methode erfolgt identisch zu dem einer Lokalen.
// Create new Bean
HelloWorld helloworld = home.create();
Die benötigten Business Methoden lassen sich nun über die erzeugte Objektinstanz aufrufen.
// Call Business Method(s)
String result = helloworld.sayHello();
5. Aufruf
4. Aufruf
Remote
Interface
Client
obj
3. Referenz auf EJB Object
obj
1. Anfrage
nach Referenz
© 2008 Stephan Metzler
Inside EJB
Remote
Objekt
Home
Interface
Home
Objekt
Enterprise
Java Beans
obj
EJB - Kontainer
2. Erzeuge
EJB-Object
Version 3.0
Seite 28 von total 218 Seiten
J2EE – EJB 2.x
5.3.1
Sequenz Diagramm
Client
JNDI Service
EJBHome
EJBObject
lookup
reference to EJBHome
create / find
reference to EJBObject
business methods
5.4
RMI (Remote Method Invocation)
Remote Method Invocation (RMI, deutsch etwa "Aufruf entfernter Methoden"), ist der Aufruf
einer Methode eines entfernten Java-Objekts und realisiert die Java-eigene Art eines sog. RPC
(Remote Procedure Call). "Entfernt" bedeutet dabei, dass sich das Objekt in einer anderen
virtuellen Maschine befinden kann (die ihrerseits auf einem entfernten Rechner oder auf dem
lokalen Rechner laufen kann). Dabei sieht der Aufruf für das aufrufende Objekt (bzw. dessen
Programmierer) genauso aus, wie ein lokaler Aufruf, es müssen jedoch besondere Ausnahmen
abgefangen werden, die zum Beispiel einen Verbindungsabbruch signalisieren können.
Auf der Client-Seite kümmert sich der sogenannte Stub – eine mit dem RMI-Compiler rmic
erzeugte Klasse – um den Netzwerktransport. Der Stub muss entweder lokal oder über das
Netz für den Client verfügbar sein.
Entfernte Objekte können zwar auch von einem bereits im Programm bekannten entfernten
Objekt bereitgestellt werden, für die erste Verbindungsaufnahme werden aber die Adresse des
Servers und ein Bezeichner (ein RMI-URL) benötigt. Für den Bezeichner liefert ein
Namensdienst auf dem Server eine Referenz auf das entfernte Objekt zurück. Damit dies
funktioniert, muss das entfernte Objekt im Server sich unter diesem Namen zuvor beim
Namensdienst registriert haben. Der RMI-Namensdienst wird über statische Methoden der
Klasse java.rmi.Naming angesprochen. Der Namensdienst ist als eigenständiges Programm
implementiert und wird RMI Registry genannt.
Kommunikationsprotokoll
RMI bezeichnet ausserdem ein Kommunikationsprotokoll, das für entfernte Aufrufe zwischen
Java-Objekten verwendet wird, und eine Java-Standard-Klassenbibliothek, mit der diese
Aufrufe realisiert werden können. Diese Klassenbibliothek ist Teil der Java 2 Standard Edition
(J2SE). Alternativ kann auch IIOP als Kommunikationsprotokoll eingesetzt werden. Für RMI
sind zwei Ports reserviert. Port 1099 ist für die RMI-Registry reserviert, also den Namensdienst.
Port 1098 für den Activator reserviert.
© 2008 Stephan Metzler
Inside EJB
Version 3.0
Seite 29 von total 218 Seiten
J2EE – EJB 2.x
5.4.1
RMI Kommunikationsmodell
Client
Stub
Remote
Reference
Layer
virtuelle Kommunikation
Server
Skeleton
Remote
Reference
Layer
reelle Kommunikation
Netzwerkverbindung
Ablauf
1. Der Server registriert ein Remote Object bei der RMI-Registry unter einem eindeutigen
Namen.
2. Der Client schaut bei der RMI-Registry unter diesem Namen nach und bekommt eine
Objektreferenz, die seinem Remote Interface entsprechen muss.
3. Der Client ruft eine Methode aus der Objektreferenz auf (dass diese Methode existiert, wird
durch das Remote Interface garantiert).
4. Der Server gibt dem Client die Rückgabewerte dieses Aufrufes, oder der Client bekommt
eine Fehlermeldung (z. B. bei einem Verbindungsabbruch).
Activation
Als Ergänzung von RMI steht die sogenannte Activation ("Aktivierung") zur Verfügung. Ähnlich
der RMI-Registry ist auch dies eine zentrale Anlaufstelle für RMI-Clients. Jedoch kann der
Activator auch RMI-Server-Objekte erzeugen und neue Virtuelle Maschinen starten.
Anwendung
Bei der Kommunikation mit RMI hat der Client den Eindruck, Methoden von Objekten
aufzurufen, die auf dem Server liegen. Folgende Kriterien sind Bestandteile von RMI:
- Nahtlose Unterstützung entfernter Aufrufe von Objekten in verschiedenen VM
- Callback-Unterstützung von Server zu Applet
- Integrierung des verteilten Objekt-Modells in Java unter Beibehaltung der Java-Semantik
- Unterschiede zwischen dem verteilten Objekt Modell und dem lokalen Java Objekt Modell
sollen transparent gemacht werden
- Schreiben zuverlässiger Anwendungen so einfach wie möglich machen
- Bewahrung der Sicherheitsaspekte der JRE
Jeder Client benötigt zur Benutzung von Serverobjekten, die auf dem Server existieren, eine
Beschreibung über die Fähigkeiten eines entfernten Objektes. Deshalb implementiert jedes
Interface für entfernte Objekte, direkt oder indirekt, das Interface java.rmi.remote, damit
wird das Objekt als ein "Remote-Objekt" gekennzeichnet. In diesem Interface sind keine
Methoden definiert. Klassen, die das Interface implementieren, erlauben Clients deren
Methoden entfernt (remote) aufzurufen.
© 2008 Stephan Metzler
Inside EJB
Version 3.0
Seite 30 von total 218 Seiten
J2EE – EJB 2.x
Der Client erhält dann vom Broker-Tool (RMI-Registry) lediglich ein Stub-Objekt, das RemoteObjekt bleibt auf dem Server. Ein Stub ist eine Klasse, die das Remote-Interface implementiert
und daher für den Client als Platzhalter für den Zugriff auf das Remote-Objekt dient.
RMI - Registry
Suche Registrierung
Remote
Client
virtueller Aufruf
Objekte
Server
Der Stub kommuniziert über eine TCP-Verbindung mit dem als Skeleton bezeichneten
Gegenstück auf der Server-Seite. Das Skeleton kennt das tatsächliche Applikationsobjekt,
leitet die Anfragen des Stubs an dieses weiter und gibt den Rückgabewert an ihn zurück. Stub
und Skeleton werden während der Entwicklung mit Hilfe eines Tools generiert und verbergen
die komplizierten Details der Kommunikation zwischen Server und Client.
RMI verfolgt mit der Verteilung von Objekten im Netz einen ähnlichen Ansatz wie CORBA
(Common Object Request Broker Architecture, siehe beispielsweise http://www.omg.org).
5.5
EJB Kommunikationsmodelle
An Prozesse beteiligte EJBs kommunizieren:
- synchron, entfernt
- synchron, lokal
- asynchron, nachrichtenbasiert
© 2008 Stephan Metzler
Inside EJB
Version 3.0
Seite 31 von total 218 Seiten
J2EE – EJB 2.x
5.5.1
synchrone, entfernte Kommunikation
Aufrufe erfolgen in diesem Model als synchrone Methodenaufrufe mittels RMI-IIOP in einem
verteilten System. Die Übertragung der Daten erfolgt mittels "by-Value" Semantik, also als
Kopie der Originaldaten. Änderungen der Daten wirken sich somit nur auf die Originaldaten und
nicht auf die Kopie aus.
Ablauf:
1. EJB-Container erzeugt EJBHomeObject
2. EJB-Container registriert Home-Object beim Namensdienst
3. Client fordert vom Namensdienst Referenz auf Home-Object
4. Namensdienst liefert Referenz auf das Home-Object
5. Client fordert über das Home Interface ein EJB von Home-Object an
6. EJBHome-Object erzeugt EJBObject
7. Home-Object liefert Referenz auf EJBObject
8. Client ruft über das Remote Interface die Geschäftsmethoden der EJB auf
9. EJBObject delegiert Methodenaufruf an die EJB-Instanz
© 2008 Stephan Metzler
Inside EJB
Version 3.0
Seite 32 von total 218 Seiten
J2EE – EJB 2.x
5.5.2
synchrone, lokale Kommunikation
Aufrufe von Diensten einer EJB-Komponente erfolgen durch synchrone Methodenaufrufe. Die
Übertragung der Daten erfolgt mittels "by-Referenz" Semantik. Bei der Verwendung dieses
Kommunikations-Modells werden somit keine Kopien von Parametern an die empfangende
Komponente übergeben, sondern Adresszeiger auf die Originaldaten im Speicher. Ändern sich
die übergebenen Daten, so werden die Originaldaten überschrieben:
Ablauf:
1. EJB-Container erzeugt EJBHomeObject
2. EJB-Container registriert Home-Object beim Namensdienst
3. Client fordert vom Namensdienst Referenz auf Home-Object
4. Namensdienst liefert Referenz auf das Home-Object
5. Client fordert über das Local Home Interface ein EJB von Home-Object an
6. EJBLocalHome-Object erzeugt EJBLocalObject
7. LocalHome-Object liefert Referenz auf EJBObject
8. Client ruft über das Local Remote Interface die Geschäftsmethoden der EJB auf
9. EJBLocalObject delegiert Methodenaufruf an die EJB-Instanz
© 2008 Stephan Metzler
Inside EJB
Version 3.0
Seite 33 von total 218 Seiten
J2EE – EJB 2.x
5.5.3
asynchron, nachrichtenbasierte Kommunikation
Dient als Schnittstelle zwischen objektorientierten und nicht-objektorientierten Prozessen in
verteilten Systemen.
Ablauf:
1. Aplikationsserver registriert Nachrichtenempfänger beim Namensdienst
2. EJB-Container registriert sich als Listener für Nachrichten
3. Client fordert vom Namensdienst Referenz des Nachrichtenempfängers
4. Namensdienst liefert Referenz auf Nachrichtenempfänger
5. Client sendet Nachrichten an Nachrichtenempfänger
6. Nachrichtenempfänger übermittelt Listener eingegangene Nachricht
7. Listener delegiert Nachricht an BeanInstanz
5.5.4
Überblick über die Kommunikationsmodelle
Die folgende Tabelle weist die Aufrufmodelle den verschiedenen EJB-Typen zu:
Aufrufmodell
EJB Typen
Stateless Session Bean
Statefull Session Bean
Entity Bean
Message Driven Bean
© 2008 Stephan Metzler
Inside EJB
synchron
entfernt
synchron
lokal
asynchrony
nachrichtenbasiert
Version 3.0
Seite 34 von total 218 Seiten
J2EE – EJB 2.x
6
Stateless Session Bean ( zustandslos)
zustandslos)
modelliert einfacher Geschäftsprozess
(z.B. Zinsberechnung)
java.io.Serializable
kann keine Daten speichern
javax.ejb.EnterpriseBean
javax.ejb.SessionBean
ejbActivate()
ejbPassivate()
ejbRemove()
setSessionContext()
haben keine Identität
Aufgabe muss mit einem Methodenaufruf erledigt
sein, zweimaliger Aufruf des Beans garantiert nicht
dieselbe Instanz auf dem Server
eine create() Methode, parameterlos
nutzt nicht alle Methoden
pooling:
- mehrere Clients rufen dasselbe Bean auf
mySessionBean
ejbCreate()
business methods
6.1
- gemeinsame Nutzung von Sourcen
- weniger Objekte im Einsatz
- ABER:
Bean ist single-threaded, ein Bean kann
nur ein Client behandeln
Bean Life Cycle
does not exist
Container initiated:
ejbRemove()
Methode-Ready-Pool
Container initiated:
Class.newInstance()
setSessionContext(sc)
ejbCreate()
Client initiated:
business methods
Wird unmittelbar nach der Instanzierung der Bean durch den Container
aufgerufen um der Bean ihren SessionContext mitzuteilen. Das übergebene
setSessionContext Objekt vom Typ SessionContext definiert die Umgebung der Bean-Instanz.
Darüber erhält die Bean z.B. Informationen über den aufrufenden Client
(Sicherheitsaspekte).
ejbCreate
Wird nach der Methode setSessionContext durch den Container aufgerufen,
um die Bean zu initialisieren, z.B. Verbindungen zu anderen Objekten und
Datenbanken.
ejbRemove
Wird durch den Container aufgerufen, nachdem der Client die Methode
remove des Remote Objektes aufgerufen hat. In dieser Methode sollten die
für die Bean reservierten Ressourcen wieder freigegeben werden. Danach
löscht der Container die Bean-Instanz.
© 2008 Stephan Metzler
Stateless Session Bean ( zustandslos)
Version 3.0
Seite 35 von total 218 Seiten
J2EE – EJB 2.x
6.2
Übung ee1 : Zinsberechnung
Erstellen und deployen Sie ein Stateless Session Bean, das als Businessmethode die
Zinsberechnung eines Geldbetrages amount über eine definierte Laufzeit years zu einem
gegebenen Zinssatz rate mehreren Klienten zur Verfügung stellt. Die Attribute amount und
years müssen clientspezifisch definiert werden. Der Zinssatz rate kann als ein Attribut der
EJB oder clientspezifisch definiert werden.
Ansatz:
Entwicklung einer
Geldanlage durch
Zins und Zinseszins:
© 2008 Stephan Metzler
Stateless Session Bean ( zustandslos)
Version 3.0
Seite 36 von total 218 Seiten
J2EE – EJB 2.x
7
Stateful Session Bean ( zustandsbehaftet )
java.io.Serializable
javax.ejb.EnterpriseBean
modelliert Geschäftsprozess
(z.B. Shopping Cart)
kann Daten speichern
Zustandsbehaftet
(kann den Zustand eines best. Client speichern)
javax.ejb.SessionBean
ejbActivate()
ejbPassivate()
ejbRemove()
setSessionContext()
mehrmaliger Aufruf des Beans für einen
Geschäftsablauf möglich
mehrere create() Methoden
(mit unterschiedlichen Signaturen)
kein pooling
passivated:
mySessionBean
ejbCreate()
business methods
7.1
- wenig Ressourcen und Memory vorhanden
- bessere Performance
Bean Life Cycle
does not exist
Container initiated:
Class.newInstance()
timeout
Container initiated:
ejbRemove()
setSessionContext(sc)
ejbCreate()
Client initiated:
remove()
Client initiated:
create()
Methode-Ready-Pool
Client initiated:
business methods
Container initiated:
ejbActivate()
ejbPassivate()
Passive
ejbPassivate
Wird vom Container aufgerufen, bevor die Bean zeitweilig aus dem Speicher
entfernt wird. Die Bean kann die von ihr in Anspruch genommenen
Ressourcen freigeben. Die Bean bleibt aber weiterhin dem Client zugeordnet.
Für den Client ist der Aufruf dieser Methode transparent.
ejbActivate
Wird vom Container aufgerufen, nachdem der Client eine Methode des
Remote-Interface aufgerufen hat. Der Container holt die passivierte Instanz
der Bean in den Arbeitsspeicher zurück und ruft danach die Methode
ejbActivate auf, um Zustand der Bean erneut zu initialisieren.
ejbRemove
Wird durch den Container aufgerufen, nachdem der Client die Methode
remove des Remote Objektes aufgerufen hat. In dieser Methode sollten die
für die Bean reservierten Ressourcen wieder freigegeben werden. Danach
löscht der Container die Bean-Instanz.
© 2008 Stephan Metzler
Stateful Session Bean ( zustandsbehaftet )
Version 3.0
Seite 37 von total 218 Seiten
J2EE – EJB 2.x
7.2
Übung ee2 : Kontoführung
Erstellen und deployen Sie ein Stateful Session Bean zur Kontoführung. Benützen Sie dazu die
Variable private float accountBalance:
- Betrag deponieren (als Float)
- Betrag abheben (als Float)
- Saldo ausgeben (auf Konsole)
7.2.1
Konsolen Statements
Ergänzen Sie die Bean Methoden mit Konsolen Statements zur Überwachung der Aktivitäten
des Applikations-Servers.
7.2.2
Environment Entries als InitialContext
Erweitern Sie die Business Logik und führen Sie auf allen Konten einen Zinssatz. Dabei ist es
sinnvoll für alle Konten denselben Zinssatz zu definieren. Benützen Sie dazu die Variable
private float rate:
- Zins (im Deployment Descriptor) als Environment Entry festlegen
- Suchen Sie nachdem das Bean deployed ist den entsprechenden Eintrag im DD und ändern
Sie diesen ohne das Bean neu zu deployen.
Ansatz / Fragmente:
public void setSessionContext(SessionContext sc) {
try {
InitialContext ic = new InitialContext();
Context local = (Context) ic.lookup("java:comp/env");
Float interestrate = (Float) local.lookup("Zins");
rate = interestrate.floatValue();
System.out.println("TellerBean context set!");
System.out.println("NEW RATE SET TO: " + rate);
} catch (javax.naming.NamingException e) {
e.printStackTrace();
}
System.out.println("TellerBean context set!");
}
Setzen des Environment Entry für "Zins" im Deploymenttool:
Generierter Eintrag im DD ( ejbejb-jar.xml )
<env-entry>
<env-entry-name>Zins</env-entry-name>
<env-entry-type>java.lang.Float</env-entry-type>
<env-entry-value>1.0</env-entry-value>
</env-entry>
© 2008 Stephan Metzler
Stateful Session Bean ( zustandsbehaftet )
Version 3.0
Seite 38 von total 218 Seiten
J2EE – EJB 2.x
8
Entity Bean ( für die Abbildung von Daten einer DB )
java.io.Serializable
javax.ejb.EnterpriseBean
javax.ejb.EntityBean
ejbActivate()
ejbPassivate()
setEntityContext()
unsetEntityContext()
ejbLoad()
ejbStore()
myEntityBean
ejbCreate()
ejbPostCreate()
ejbFindXXX()
modelliert Abbildungen von persistenten
Geschäftsdaten (DB)
mehrere Clients können auf diese Entities
zugreifen
verwendet identifizierbare Daten (primary key)
CMP - Container Managed Persistence:
- Container speichert Bean in DB
- Bean-Provider muss sich nicht kümmern
- exakter Mechanismus ist nicht definiert !
- jedes Objekt hat einen Primary Key
BMP - Bean Managed Persistence:
- Bean-Provider ist verantwortlich, dass
die Daten in der DB gespeichert werden
- Realisation mittels SQL, ...
- höherer Aufwand als CMP
- mehr Flexibilität als CMP
business methods
8.1
Bean Life Cycle
Container initiated:
unsetEntityContext()
Object.finalize()
does not exist
Container initiated:
Class.newInstance()
setEntityContext()
Pooled
Container initiated:
ejbPassivate()
ejbRemove()
Client initiated:
remove()
Container initiated:
ejbActivate()
ejbCreate(),ejbPostCreate()
Client initiated:
create()
Ready
Container initiated:
ejbLoad(), ejbStore()
Client initiated:
getter / setter
Pooled
Die Instanz existiert, aber sie besitzt keine Indentität. Wenn der Client eine findoder create-Methode des Home Interface aufruft, wird einer Bean Instanz aus
dem Pool vom Kontainer eine Identität (Primärschlüssel) zugewiesen.
Ready
Die Instanz existiert, besitzt eine Identität und kann vom Client benutzt werden.
© 2008 Stephan Metzler
Entity Bean ( für die Abbildung von Daten einer DB )
Version 3.0
Seite 39 von total 218 Seiten
J2EE – EJB 2.x
8.1.1
EJB-Container Life-Cycle- Methoden
Life Cycle Methode
Beschreibung
setEntityContext()
unsetEntityContext() wird aufgerufen nach erstellen / vor entfernen der Entity Bean
8.1.2
ejbLoad()
ejbStore()
ladet / speichert die Entity Bean
ejbRemove()
entfernt die Entity Bean
ejbActivate()
ejbPassivate()
aktiviert / passiviert die Entity Bean
Persistenzoperationen einer Entity Bean
Operation:
ejbCreate
SQL Alias:
INSERT
Operation:
ejbFindByPrimaryKey
SQL Alias:
SELECT
Wird nach dem Erzeugen eines Java-Objektes (EJB) aufgerufen
um dieses in der Datenbank abzulegen.
Diese Operation ist nicht Bestandteil der Schnittstelle, da ihre
Parameter, die den Übergabeparametern des Objektkonstruktors
entsprechen, zum Schnittstellenerzeugungszeitpunkt nicht
feststehen.
Liefert den Wert des Primärschlüssels zurück, sofern ein
Datenbankeintrag existiert, der durch diesen Primärschlüssel
identifiziert wird.
Diese Operation ist nicht Bestandteil der Schnittstelle, da ihre
Parameter, die in Typ, Name und Reihenfolge der
Zusammensetzung des Primärschlüssels entsprechen, zum
Schnittstellenerzeugungszeitpunkt nicht feststehen.
Diese Methode wird nicht durch den Anwender direkt aufgerufen,
sondern (stattdessen auf einer Ausprägung der HomeSchnittstelle) das zur Verfügung stehende Analog
findByPrimaryKey, welches das durch den Primärschlüssel
identifizierte EJB-Objekt zurückliefert.
Diese Methode greift intern auf ausschliesslich den Schlüssel
liefernde Methode der Bean zu.
Operation:
ejbRemove
SQL Alias:
Entfernt das EJB-Objekt aus der Datenbank.
DELETE
Operation:
ejbStore
SQL Alias:
UPDATE
Synchronisiert das Java-Objekt mit dem EJB-Objekt und
aktualisiert so die Datenbankinhalte.
Diese Methode wird nach jedem Zugriff mittels einer in der
Remote-Schnittstelle aufgeführten Operation auf das EJB-Objekt
ausgeführt.
© 2008 Stephan Metzler
Entity Bean ( für die Abbildung von Daten einer DB )
Version 3.0
Seite 40 von total 218 Seiten
J2EE – EJB 2.x
8.2
Modellieren von persistenten Geschäftsdaten
8.2.1
Übung ee3 : Personenverwaltung mit CMP 1.0
SUN ™ Applikationsserver
CMP (1.x) Entity Bean
public id
public name
public fname
DB: EnterpriseEducation
PERSONBEAN
ID
FNAME
NAME
HOME Interface einer Entity Bean
- Deklariert die Bean-Lifecycle-Methoden
- Erweitert javax.ejb.EJBHome (das wiederum java.rmi.Remote erweitert)
- Klasse, die dieses Interface implementiert (EJB Home) wird automatisch generiert
public interface PersonHome extends EJBHome {
// create und finder Methoden deklarieren
public Person create(String id, String name, String fname)
throws CreateException, RemoteException;
// entsprechender Return Type festlegen ( [0..1] -> Person )
public Person findByPrimaryKey(String key)
throws FinderException, RemoteException;
// entsprechender Return Type festlegen ([0..*] -> Collection )
public Collection findAll() throws FinderException,
RemoteException;
}
REMOTE Interface einer Entity Bean
-
Deklariert die Bean-Business-Methoden
Erweitert javax.ejb.EJBObject (das wiederum java.rmi.Remote erweitert)
Klasse, die dieses Interface implementiert (EJB Object) wird automatisch generiert
Wrapper des eigentlichen Beans
public interface Person extends EJBObject {
// public setter und getter Methoden der Entitäten deklarieren
public void setId(String id) throws RemoteException;
public void setName(String name) throws RemoteException;
public void setFname(String fname) throws RemoteException;
public String getName() throws RemoteException;
public String getFname() throws RemoteException;
public String getId() throws RemoteException;
}
© 2008 Stephan Metzler
Entity Bean ( für die Abbildung von Daten einer DB )
Version 3.0
Seite 41 von total 218 Seiten
J2EE – EJB 2.x
BEAN Implementation einer Entity Bean
- Implementiert alle Methoden, die für den Lebenszyklus benötigt werden
- Signatur muss identisch sein mit der des Remote & Home Interfaces
- Implementiert javax.ejb.EntityBean ( das wiederum javax.ejb.EnterpriseBean erweitert)
public class PersonBean implements EntityBean {
// DB Felder als public Attribute definieren
public String id = "";
public String name = "";
public String fname = "";
// public setter und getter Methoden der Entitäten definieren
public String getId() {return id;}
public void setId(String id) {this.id = id;}
...
// Für jede create() Methode des Home Interfaces muss es eine
// entsprechende ejbCreate() Methode mit derselben Signatur geben
public String ejbCreate(String id, String name, String fname) {
// Belegen der Bean Attribute mit den Übergabeparameter
this.id = id;
this.name = name;
this.fname = fname;
// Rückgabewertes ist Primary_Key Attribute
return id;
}
// Für jede create() Methode des Home Interfaces muss es eine
// entsprechende ejbPostCreate() Methode mit derselben Signatur
// geben
public void ejbPostCreate(String id, String name, String fname) {
}
// sämtliche Kontainer Life Cycle Methoden müssen definiert werden
public void setEntityContext(EntityContext arg0)
throws EJBException, RemoteException {
}
public void unsetEntityContext() throws EJBException,
RemoteException {
}
...
}
Deployment
Die Persistenz-Einstellungen im Deploytool sind
im Entity TAB untergebracht:
- Persistente Felder bestimmen
- Primary Key Class Feld bestimmen
- CMP Database definieren
© 2008 Stephan Metzler
Entity Bean ( für die Abbildung von Daten einer DB )
Version 3.0
Seite 42 von total 218 Seiten
J2EE – EJB 2.x
mySQL Datasource anbinden mit:
- Connection Pool (im Sun AS™ definieren)
- JDBC Datasource (im Sun AS™ über JNDI definiert)
- CMP Database Mapping (in Deployment Tool)
Mit der admin Konsole des Sun AS™ ( http://localhost:4848 ) eine neue JDBC Resource und
ein entsprechender Connection Pool erstellen:
Die notwendige MySQLDriver.class ist vom
entsprechenden DataSource-Vendor zu
beziehen und dem Sun AS™ zur Verfügung
zu stellen:
© 2008 Stephan Metzler
Entity Bean ( für die Abbildung von Daten einer DB )
Version 3.0
Seite 43 von total 218 Seiten
J2EE – EJB 2.x
CMP Database Mapping
per JNDI "jdbc/ee"
(entsprechend Sun
AS™ JDBC Ressource):
Das Deploymenttool erstellt dann die entsprechenden xml Dateien wie auch ein DatabaseSchema (Person_ejb-jar-ic.dbschema ) für das Erstellen der entsprechenden Tabelle:
Deployment Descriptor ejbejb-jar.xml (Ausschnitt)
<enterprise-beans>
<entity>
<ejb-name>PersonBean</ejb-name>
<home>ejb.PersonHome</home>
<remote>ejb.Person</remote>
<ejb-class>ejb.PersonBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>false</reentrant>
<cmp-version>1.x</cmp-version>
<cmp-field><field-name>fname</field-name></cmp-field>
<cmp-field><field-name>name</field-name> </cmp-field>
<cmp-field><field-name>id</field-name></cmp-field>
<primkey-field>id</primkey-field>
</entity>
Deployment Descriptor sunsun-cmpcmp-mappings.xml (Ausschnitt)
<sun-cmp-mappings>
<sun-cmp-mapping>
<schema>Person_ejb-jar-ic</schema>
<entity-mapping>
<ejb-name>PersonBean</ejb-name>
<table-name>PERSONBEAN</table-name>
<cmp-field-mapping>
<field-name>fname</field-name>
<column-name>PERSONBEAN.FNAME</column-name>
</cmp-field-mapping>
<cmp-field-mapping>
<field-name>id</field-name>
<column-name>PERSONBEAN.ID</column-name>
</cmp-field-mapping>
<cmp-field-mapping>
<field-name>name</field-name>
<column-name>PERSONBEAN.NAME</column-name>
</cmp-field-mapping>
</entity-mapping>
</sun-cmp-mapping>
</sun-cmp-mappings>
© 2008 Stephan Metzler
Entity Bean ( für die Abbildung von Daten einer DB )
Version 3.0
Seite 44 von total 218 Seiten
J2EE – EJB 2.x
8.2.2
CMP/CMR in EJB 2.0
Enterprise JavaBeans (EJB) 2.0 erweitern die frühere Version 1.x mit folgenden Eigenschaften:
erweiterte Funktionalität für Container-Managed Persistence für Entity Beans
unterstützt CMR (Container-Managed Relationships)
definiert EJB-Query Language (EJB-QL) für select und find Query Methoden
unterstützt zusätzlich Locale Remote and Locale Home Interfaces um den Zugriff zwischen
Beans im gleichen Container zu ermöglichen
Die Funktionsweise und die wesentlichen Unterschiede zu CMP 1.0 sind an einer trivialen EJB
Kundenverwaltung erläutert. Ein Kunde besteht aus einem Namen und einer Adresse. Dafür
werden zwei Entitäten geführt, realisiert durch zwei Entity Beans (CMP 2.0). Die Relation dieser
beiden Entitäten wird in EJB's definiert.
-
8.2.3
Übung ee31 : Triviale Kundenverwaltung mit Entity Bean (CMP 2.0)
Das Besipiel definiert zwei CMP Entity Beans, die zwei Tabellen "Name" (Name, Vorname) und
"Adresse" (Strasse, Ort) persistiert. Die 1:1 Relation zwischen den beiden Entitäten wird durch
den EJB Kontainer realisiert:
Applikationsserver
CMP (2.x) EntityBean:Name
get
get
get
get
/
/
/
/
set
set
set
set
DB: EnterpriseEducation
NAMEBEAN
ID
NAME
FNAME
id
name
fname
address
1:1
CMP (2.x) EntityBean:Address
ADDRESSBEAN
ID STREET LOCATION
Relation
get / set id
get / set street
get / set location
Local HomeHome-Interface der Address Bean (Entity Bean)
Es werden im Gegensatz zum Remote-Interface keine java.rmi.RemoteException geschmissen.
public interface AddressLocalHome extends EJBLocalHome {
public AddressLocal create(String street, String location)
throws CreateException;
// Methode findByPrimaryKey(...) muss deklariert werden
public AddressLocal findByPrimaryKey(String id)
throws FinderException;
// optionale finder-Methoden können folgen
}
© 2008 Stephan Metzler
Entity Bean ( für die Abbildung von Daten einer DB )
Version 3.0
Seite 45 von total 218 Seiten
J2EE – EJB 2.x
LocalLocal-Interface zur Definition der Geschäftsmethoden der Address Bean (Entity Bean).
Die Geschäftsmethoden umfassen nur getter/setter-Methoden für die Attribute der Entity
Bean. Die getter/setter-Methoden schmeissen im Gegensatz zum Remote-Interface keine
RemoteException.
public interface AddressLocal extends EJBLocalObject {
public String getStreet();
public String getLocation();
}
BeanBean-Klasse der Entity Bean (mit CMP 2.0) Address.
Es werden alle im Local-Interface (AddressLocal.java) definierten Geschäftsmethoden und auch
bestimmte callback-Methoden implementiert. Wesentliche Unterschiede zu CMP 1.0:
- Attribute einer Addresse sind nicht mehr als Felder in der Java-Klasse deklariert
- getter/setter-Methoden als abstrakte Methoden deklariert
(werden vom Container implementiert)
- die Bean-Klasse ist folglich auch abstrakt
public abstract class AddressBean implements EntityBean {
private EntityContext context;
// abstrakte getter/setter-Methoden - PERSISTENTE FELDER
public abstract String getId();
public abstract void setId(String id);
public abstract String getStreet();
public abstract void setStreet(String street);
public abstract String getLocation();
public abstract void setLocation(String location);
// Callback-Methoden (werden bei Bedarf vom Container aufgerufen)
// Diese Methoden sind grösstenteils leer implementiert, da
// Container-Managed Persistence verwendet wird
// ejbCreate(...) legt eine neue Adresse an
// Diese Methode wird vom Container automatisch aufgerufen, sobald
// create(.) auf dem Home-Object aufgerufen wird.
// Die Signature von create(...) und ejbCreate(...) muss übereinstimmen
// @return null
public String ejbCreate(String street, String location)
throws CreateException {
System.out.println("AddressEJB: ejbCreate called");
setId(street + location);
setStreet(street);
setLocation(location);
return null;
}
// Lifecycle Methoden und Default Kunstruktor
public void ejbPostCreate(String street, String location) {
System.out.println("AddressEJB: ejbPostCreate called");
}
public void ejbRemove() {
System.out.println("AddressEJB: ejbRemove called");
}
...
public AddressBean() {
}
}
© 2008 Stephan Metzler
Entity Bean ( für die Abbildung von Daten einer DB )
Version 3.0
Seite 46 von total 218 Seiten
J2EE – EJB 2.x
Local HomeHome-Interface der Name Bean (Entity Bean)
Es werden im Gegensatz zum Remote-Interface keine java.rmi.RemoteException geschmissen.
public interface NameLocalHome extends EJBLocalHome {
public NameLocal create(String id, String street,
String location) throws CreateException;
// Methode findByPrimaryKey(...) muss deklariert werden
public NameLocal findByPrimaryKey(String id)
throws FinderException;
// optionale finder-Methoden können folgen
}
LocalLocal-Interface zur Definition der Geschäftsmethoden der Name Bean (Entity Bean).
Die Geschäftsmethoden umfassen nur getter/setter-Methoden für die Attribute der Entity
Bean. Die getter/setter-Methoden schmeissen im Gegensatz zum Remote-Interface keine
RemoteException.
public interface NameLocal extends EJBLocalObject {
public AddressLocal getAddress();
public void setAddress(AddressLocal address);
}
Client--Zugriff
(Remote) Home Interface der Name Bean (ermöglicht einen Client
Zugriff via RMIRMI-IIOP)
public interface NameHome extends EJBHome {
public Name create(String id, String name, String fname,
String street, String location) throws CreateException,
RemoteException;
// Methode findByPrimaryKey(...) muss deklariert werden
public Name findByPrimaryKey(String id) throws FinderException,
RemoteException;
// optionale finder-Methoden können folgen
}
(Remote) Interface der Business Logik der Name Bean (ermöglicht Zugriff via RMIRMI-IIOP)
public interface Name extends EJBObject {
public String getFullName() throws RemoteException;
public String getFullAddress() throws RemoteException;
}
BeanBean-Klasse der Entity Bean (mit CMP 2.0) Name.
Es werden alle im Local-Interface (NameLocal.java) und im Remote-Interface (Name.java)
definierten Geschäftsmethoden und auch bestimmte callback-Methoden implementiert.
Unterschiede zu CMP 1.0:
- Attribute einer Addresse sind nicht mehr als Felder in der Java-Klasse deklariert
- getter/setter-Methoden als abstrakte Methoden deklariert
(werden vom Container implementiert)
- die Bean-Klasse ist folglich auch abstrakt
public abstract class NameBean implements EntityBean {
public NameBean() {
}
© 2008 Stephan Metzler
Entity Bean ( für die Abbildung von Daten einer DB )
Version 3.0
Seite 47 von total 218 Seiten
J2EE – EJB 2.x
// Nicht PERSISTENTE FELDER
private EntityContext context;
private AddressLocalHome addressLocalHome;
// abstrakte getter/setter-Methoden - PERSISTENTE FELDER
public abstract String getId();
public abstract void setId(String id);
public abstract String getName();
public abstract void setName(String name);
...
//abstrakte getter/setter-Methoden - RELATIONEN
public abstract AddressLocal getAddress();
public abstract void setAddress(AddressLocal address);
// Callback-Methoden (werden bei Bedarf vom Container aufgerufen)
// Diese Methoden sind grösstenteils leer implementiert, da wir
// Container-Managed Persistence verwenden
// ejbCreate(...) legt ein neues Produkt an
// Diese Methode wird vom Container automatisch aufgerufen, sobald
// create(.) auf dem Home-Object aufgerufen wird. Die Argumente (Typen)
// von create(.) und ejbCreate(.) müssen übereinstimmen.
// @return null
public String ejbCreate(String id, String name, String fname,
String street, String location) throws CreateException {
System.out.println("ejbCreate called");
setId(id);
setName(name);
setFname(fname);
return null;
}
public void ejbPostCreate(String id, String name, String fname,
String street, String location) throws CreateException {
System.out.println("ejbPostCreate called");
AddressLocal address = addressLocalHome.create(street, location);
setAddress(address);
}
// Business Methoden des REMOTE INTERFACE
public String getFullName() {
return getName() + ", " + getFname();
}
public String getFullAddress() {
return getAddress().getStreet() + ", " + getAddress().getLocation();
}
// LIFECYCLE METHODEN
public void setEntityContext(EntityContext context) {
System.out.println("setEntityContextCalled");
this.context = context;
try {
Context ctx = new InitialContext();
Context localCtx = (Context) ctx.lookup("java:comp/env");
Object obj = localCtx.lookup("ejb/address");
addressLocalHome = (AddressLocalHome) PortableRemoteObject
.narrow(obj, AddressLocalHome.class);
} catch (NamingException e) {throw new EJBException(e);
}
}
...
}
© 2008 Stephan Metzler
Entity Bean ( für die Abbildung von Daten einer DB )
Version 3.0
Seite 48 von total 218 Seiten
J2EE – EJB 2.x
Deployment:
Relevante Einstellung im Deploytool:
- EJB Referenz
- Relationship
Diese Einstellungen resultieren in
folgendem Eintrag im Deployment
Descriptor ejb-jar.xml (Ausschnitt)
Im weiteren bildet das Deploytool
sämtliche Interface Methoden der
Remote und Locale Interfaces ab und
definiert die benutzerspezifischen
Parameter wie Transaction, Security, etc.
Deployment Descriptor: ejbejb-jar.xml (Ausschnitt)
…
<relationships>
<ejb-relation>
<ejb-relationship-role>
<multiplicity>One</multiplicity>
<relationship-role-source>
<ejb-name>NameBean</ejb-name>
</relationship-role-source>
<cmr-field>
<cmr-field-name>address</cmr-field-name>
</cmr-field>
</ejb-relationship-role>
<ejb-relationship-role>
<multiplicity>One</multiplicity>
<cascade-delete/>
<relationship-role-source>
<ejb-name>AddressBean</ejb-name>
</relationship-role-source>
</ejb-relationship-role>
</ejb-relation>
</relationships>
© 2008 Stephan Metzler
Entity Bean ( für die Abbildung von Daten einer DB )
Version 3.0
Seite 49 von total 218 Seiten
J2EE – EJB 2.x
C
Best Practice
9
Design Patterns
Patterns
9.1
Einführung
Dieses Dokument gibt einen ersten Einblick in die Software Design Patterns und erläutert
deren Sinn und Zweck. Die Einführung in die Welt der Software Design Patterns wird dabei
anhand von drei der häufig verwendeten Patterns gemacht, dem Proxy-, dem Singleton- und
dem Factory Method Pattern.
9.1.1
Zweck
Pattern heisst auf Deutsch übersetzt Muster oder Beispiel. Design Patterns stellen
wiederverwendbare Lösungen zu häufig auftretenden Problemen in der Software Entwicklung
dar. Sie kommen aus der Praxis und wiederspiegeln die Erfahrung von Experten. Die
Verwendung solcher Muster erhöht die Qualität der Software und erleichtert die Kommunikation
zwischen den einzelnen Entwicklern.
9.1.2
Geschichtliches
Die Idee für die Verwendung von Patterns (Mustern) stammt ursprünglich aus der Architektur.
Im Jahre 1977 hat Christopher Alexander ein Buch über die Verwendung von Mustern in der
Architektur geschrieben.
Ward Cunningham und Kent Beck nahmen 1987 die Idee von Alexander auf und entwarfen
fünf Patterns für die User-Interface Programmierung.
Der endgültige Durchbruch der Software Patterns gelang jedoch erst in den 90er Jahren. Im
Jahr 1994 wurde das Buch Design Patterns von den Autoren Erich Gamma, Richard Helm,
John Vlissides und Ralph Johnson geschrieben. Design Patterns entwickelte sich zu einem der
Einflussreichsten Computer Bücher überhaupt und ist auch unter dem Begriff Gang of Four
bekannt.
Die drei ersten in diesem Dokument vorgestellten Patterns (Proxy-, Singleton- und Factory
Method Pattern) stammen aus dem Design Patterns Buch.
9.1.3
Referenzen
- Gamelan
http://www.developer.com/java/
- Gamma E., Helm R., Johnson R., Vlissides J.
(1994), Design Patterns, Reading, Addison-Wesley
- Grand M.
(1998), Patterns in Java Volume 1, New York, Wiley Computer Publishing
- Javaworld
Javaworld
http://www.javaworld.com
- Bien, Adam
( 2003 ), J2EE Patterns, Addison-Wesley
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 50 von total 218 Seiten
J2EE – EJB 2.x
9.2
Singleton Pattern
[Deutsch: Einzelfall, Einzelkarte]
Das Singleton Pattern gehört zur Kategorie der creational patterns. Die creational patterns
werden immer dann angewendet, wenn bei der Erzeugung von Objekten eine Entscheidung
verlangt wird. Das Singleton Pattern stellt sicher, dass nur eine Instanz einer Klasse erzeugt
wird und ein geeigneter Zugriffsmechanismus zur Verfügung steht.
9.2.1
Problem
Es ist oft notwendig, dass es von einer Klasse exakt eine Instanz gibt. Beispiele hierfür sind:
Print-Spoolers, Datenbank-Manager oder Zugriffe auf das Dateisystem. Ein solches Problem
wird typischerweise mit dem Singleton Pattern gelöst. Das Singleton Pattern weist die
folgenden Eigenschaften auf:
- Es garantiert, dass nur eine Instanz einer Klasse erzeugt wird.
- Es stellt einen globalen Zugriffsmechanismus auf das Objekt zu Verfügung.
9.2.2
Lösung
Wie ersichtlich ist, besteht das Singleton Pattern aus nur einer Klasse und ist daher relativ
einfach. Die Singleton Klasse hat eine statische Variable, welche auf die einzige Instanz der
Klasse zeigt. Mittels der statischen Methode getInstance() wird eine Referenz auf die
Instanz der Klasse zurückgegeben.
Singleto n
instanc e
Singleton getInstanc e()
operatio ns
9.2.3
Implementation
In diesem Unterkapitel wird die Umsetzung des Patterns in Java aufgezeigt.
Der "Klassische Singleton"
Als erstes wird die klassische Umsetzung des Singleton Pattern in Java angeschaut:
public class ClassicSingleton {
private static ClassicSingleton instance = null;
private ClassicSingleton() {
// Exists only to defeat instantiation.
}
public static ClassicSingleton getInstance() {
if(instance == null) {
instance = new ClassicSingleton();
}
return instance;
}
}
Der "Klassische Singleton" hat eine statische Referenz auf die einzige Singleton Instanz und
diese wird über die statische Methode getInstance() zurückgegeben. Dabei sind folgende
Betrachtungen zur Klasse ClassicSingleton zu bemerken:
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 51 von total 218 Seiten
J2EE – EJB 2.x
1. Die ClassicSingleton Klasse verwendet für die Erstellung der Instanz eine Technik
welche als "lazy instantiation" bekannt ist, d.h. dass die Instanz der Klasse (Objekt) erst
erstellt wird, wenn diese zum ersten mal gebraucht wird.
2. Der Konstruktor des ClassicSingleton ist als private deklariert. Damit wird
sichergestellt, dass nicht mehrere Instanzen der Klasse erzeugt werden können.
3. Leider ist die ClassicSingleton Klasse nicht Thread-Safe! Wenn zwei Threads
gleichzeitig die Methode getInstance() aufrufen ist es möglich, dass während der 1.
Thread eine Instanz der Klasse erzeugt, der 2. Thread gerade in diesem Moment in den IfBlock eintritt. Die Prüfung auf null ist zu diesem Zeitpunkt noch wahr und es wird somit eine
zweite Instanz der Klasse ClassicSingleton erzeugt.
Der ThreadThread-Safe Singleton:
Eine einfache Lösung, um das Singleton Pattern Thread-Safe zu programmieren, ist die
Methode getInstance() mit dem Schlüsselwort synchronized zu versehen:
public synchronized static ClassicSingleton getInstance()
Diese Lösung funktioniert ausgezeichnet. Die Synchronisation der Methode wird aber nur beim
aller ersten Mal benötigt. Da die Synchronisation bezüglich Performance eine sehr teure
Operation ist (synchronisierte Methoden können bis zu 100 Mal langsamer sein), ist somit auch
diese Lösung nicht optimal.
Eine bessere Lösung um das Singleton Pattern Thread-Safe zu programmieren, ist die
statische Variable instance gleich bei der Deklaration zu initialisieren:
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance = new ThreadSafeSingleton();
private ThreadSafeSingleton() {
// Exists only to defeat instantiation.
}
public static ThreadSafeSingleton getInstance() {
return instance;
}
}
Wie ersichtlich, wird die statische Member-Variable instance nun gleich bei der Deklaration
initialisiert. Die Java Spezifikation stellt sicher, dass jeder Compiler die Initialisierung von
statischen Membern-Variabeln nur einmal und immer beim ersten Zugriff vornimmt. Die
ThreadSafeSingleton Klasse ist somit Thread-Safe und hat eine "lazy instantiation".
Zugriff:
Zugriff:
Der Zugriff auf ein Singleton Objekt erfolgt folgendermassen:
ThreadSafeSingleton singleton = ThreadSafeSingleton.getInstance();
singleton.doThis();
singleton.doThat();
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 52 von total 218 Seiten
J2EE – EJB 2.x
9.3
Proxy Pattern
[Deutsch: Stellvertreter, Bevollmächtigter]
Das Proxy Pattern gehört zu den fundamental patterns. Bei dieser Kategorie von Patterns
handelt es sich um eine Ansammlung von grundlegenden und sehr wichtigen Patterns, wie zum
Beispiel die Delegation oder das Konzept der Interfaces. Die fundamental patterns dienen oft
als Grundlage für andere Patterns.
Das Proxy Pattern ermöglicht die Ersetzung eines Objektes durch einen Stellvertreter. Jeder
Aufruf an das Objekt erfolgt indirekt über dessen Stellvertreter.
9.3.1
Problem
Es ist oft notwendig, dass bei einem Methodenaufruf eines Objektes, zusätzliche Funktionen
ausgeführt werden müssen. Das Ausführen dieser Management-Funktion soll jedoch im
Hintergrund geschehen, so dass der Benutzer nichts davon erfährt. Es gibt eine Vielzahl solcher
Proxy Pattern Anwendungen.
Nachfolgend sind einige aufgelistet:
- Cache Proxy:
Proxy Ein Proxy ermöglicht die temporäre Speicherung von Resultaten einer teuren
Operation. Andere Benutzer können dann auch auf das Resultat zugreifen.
- Remote Proxy:
Proxy Ein Proxy erzeugt die Illusion, dass ein Objekt, welches auf einem anderen
Server existiert, ein normales lokales Objekt sei. Ein bekanntes Java-Beispiel ist die Remote
Method Invocation (RMI).
- Access Proxy:
Proxy Ein Proxy kontrolliert den Zugriff auf ein Objekt und kann an verschiedene
Benutzer unterschiedliche Zugriffsrechte vergeben.
- Synchronization Proxy:
Proxy Ein Proxy ermöglicht den gleichzeitigen Zugriff von mehreren
Benutzern auf ein Objekt.
9.3.2
Lösung
Das Proxy Pattern besteht im wesentlichen aus einer Superklasse oder einem Interface, welche
von der Proxy-Klasse wie auch der eigentlichen Service-Klasse verwendet werden. Durch diese
Abstraktion wird sichergestellt, dass die Proxy-Klasse und die Service-Klasse dem Benutzer
dieselben Methoden zur Verfügung stellen.
Der Benutzer ruft nun demnach eine Methode nicht auf dem eigentlichen Service-Objekt,
sondern auf dem Proxy-Objekt auf. Auf dem Proxy-Objekt werden die definierten
Management-Funktionen ausgeführt und der Aufruf wird an das eigentliche Service-Objekt
weitergeleitet. Eine Antwort des Service-Objektes wird ebenfalls über das Proxy-Objekt dem
Benutzer mitgeteilt.
<<inte rfa ce >>
IFService
Abstrac tServ ice
doIt()
....
doIt()
ServiceProxy
Service
do It()
do It()
0..*
© 2008 Stephan Metzler
Design Patterns
1
ServiceProxy
Service
do It()
do It()
0..*
1
Version 3.0
Seite 53 von total 218 Seiten
J2EE – EJB 2.x
9.3.3
Implementation
Ohne spezifische Management-Funktionen verwendet das Proxy Pattern nur eine Superklasse
oder ein Interface. Der Schwerpunkt bei diesem Pattern liegt deshalb auch in der Umsetzung
dieser Management-Funktionen in der Proxy Klasse.
Für die bessere Veranschaulichung des Proxy Patterns soll die Synchronisation einer Tabelle
durch einen Proxy realisiert werden. Es gilt folgende Ausgangslage:
Eine Klassenbibliothek stellt für den Zugriff auf eine Tabelle die Klasse Table zur Verfügung,
welche das Interface IFTable implementiert. Leider ist der Zugriff auf die Tabelle nicht
synchronisiert. Die Aufgabe lautet die Klasse Table zu synchronisieren, ohne den Source
Code zu verändern.
public interface IFTable {
public Object getElementAt(int row, int column);
public void setElementAt(Object element, int row, int column);
public int getNumberOfRows();
}
Die einfachste Lösung für die Implementierung einer Synchronisation ist die Verwendung des
Proxy Patterns. Das Beispiel zeigt die Klasse RowLockTableProxy, welche diese
Synchronisation vornimmt.
public class RowLockTableProxy implements IFTable {
Table realTable = null;
Integer[] locks = null;
public RowLockTableProxy(Table aRealTable) {
realTable = aRealTable;
locks = new Integer[realTable.getNumberOfRows()];
for(int row = 0; row < locks.length; row++;) {
locks[row] = new Integer(row);
}
}
public Object getElementAt(int row, int column) {
synchronized (locks[rows]) {
return realTable.getElementAt(row,column);
}
}
public Object setElementAt(Object element, int row, int column) {
synchronized (locks[rows]) {
return realTable.setElementAt(element,row,column);
}
}
public int getNumberOfRows(){
return realTable.getNumberOfRows();
}
}
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 54 von total 218 Seiten
J2EE – EJB 2.x
9.4
Factory Method Pattern
[Deutsch: Fabrik]
Das Factory Method Pattern gehört zur Kategorie der creational patterns. Das Factory Method
Pattern definiert ein Interface für die Erzeugung von Objekten, überlässt jedoch die
Entscheidung wie die Klasse instanziiert werden soll den Unterklassen.
9.4.1
Problem
Eine Applikation (oder ein Framework) kann manchmal die Klasse eines zu erzeugenden
Objektes zur Laufzeit nicht antizipieren (vorwegnehmen). Die Applikation kennt vielleicht nur die
abstrakte Klasse oder das Interface, diese lassen sich aber bekanntlich nicht instanziieren. Sie
weiss also nur wann sie ein neues Objekt erzeugen muss, nicht aber was für eine Klasse das
Objekt hat.
Ein Beispiel hierfür sind Textverarbeitungsprogramme. Ein Textverarbeitungsprogramm muss
mit einer Vielzahl von verschiedenen Dokumenttypen umgehen können. Für jeden Dokumenttyp
müssen Highlevel Methoden wie Öffnen oder Speichern ausgeführt werden können. Die
Umsetzung für jede dieser Methoden muss separat im entsprechenden Dokumententyp
implementiert werden. Ein zweites Bespiel für das Factory Method Pattern ist der Mechanismus
für das Erzeugen von Enterprise JavaBeans (EJBs).
Product
9.4.2
Lösung
UML-Klassendiagramm des
Factory Method Pattern.
Use s
*
operation1
operation2
.....
CreationRe ques tor
1
newProduct
.....
1
*
re questor
Re quests- Cre atio n
cre ator
ConcreteProduct
*
<<inte rfa ce >>
IFFactory
o pe ration1
o pe ration2
.....
cre ateP roduct(discriminator):P roduct
Creates
1
Factory
cre ateP roduct(discriminator):P roduct
Klasse
Beschreibung
Product
Die abstrakte Superklasse (oder das Interface) der Klassen, welche durch
das Factory Method Pattern instanziiert wird.
ConcreteProduct
Die konkrete Klasse. Objekte dieses Typs werden durch das Factory
Method Pattern erzeugt.
CreationRequestor Die applikationsunabhängige Klasse, welche die instanziierten Objekte der
Factory benützt.
IFFactory
Das applikationsunabhängige Interface, das die Methode deklariert,
welche zum Erzeugen der konkreten Objekte benötigt wird.
Factory
Die applikationsspezifische Klasse implementiert das passende Factory
Interface und besitzt die Methode zum Erzeugen der konkreten Objekte.
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 55 von total 218 Seiten
J2EE – EJB 2.x
9.4.3
Implementation
Die zentrale Klasse des Factory Method Pattern ist die Klasse Factory. Diese beinhaltet die
Methode createProduct(), welche die "Factory Methode" ist und dem Pattern den
Namen gibt. Damit verschiedene konkrete Produkte erzeugt werden können, benötigt die
Methode createProduct()einen Parameter zur Unterscheidung.
Das nachfolgende Beispiel zeigt eine Factory zur Erzeugung von Dokumenten. Der Methode
createProduct() wird als Parameter die Extension des Dokumentes mitgegeben. Anhand
der Extension wird das konkrete Dokument (ConcreteProduct) erzeugt und
zurückgegeben. Der Rückgabewert der Methode ist eine Referenz der abstrakten Superklasse
(Product) des konkreten Dokumentes. Diese Referenz erhält der CreationRequester
und kann so mit dem gewünschten Dokument arbeiten.
public class DocumentFactory implements IFDocumentFactory {
public Document createDocument(String ext) {
if (ext.equals("doc"))
return new WordDocument();
if (ext.equals("html"))
return new HTMLDocument();
}
}
Das nachfolgende Beispiel zeigt eine Factory, welche überhaupt keine Angaben über
bestehende Klassen zur Kompilierungszeit benötigt.
public class DynDocumentFactory implements IFDocumentFactory {
public Document createDocument(String docClassName)
throws Exceptions {
Class docClass = Class.forName(docClassName);
return (Document)docClass.newInstance();
}
}
Ein Beispiel für die Verwendung des Factory Method Pattern ist die Erzeugung von EJBs.
Dabei ist das Home Interface EJBHome (und deren Containerspezifische Implementierung) die
Factory. Die Methoden create() bei einem Session Bean oder find() bei einem Entity
Bean sind die entsprechenden Factory Methoden, welche das gewünschte Bean erzeugen.
// get the JNDI naming context and lookup the EJB Home interface
Context initialCtx = new InitialContext()
HelloWorldHome home =(HelloWorldHome)initialCtx.lookup("HelloWorld");
// use the Home Interface to create a bean object --> Factory Method
// Pattern
HelloWorld helloworld = home.create();
// invoke the business methods through the interface reference
helloworld.sayHello();
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 56 von total 218 Seiten
J2EE – EJB 2.x
9.5
J2EE Pattern
Im Zusammenhang mit J2EE Applikationen sind spezielle Anforderungen erwünscht wie:
- Transparenz
- Stabilität
- Performance
Auch hierzu existieren Entwurfsmuster, die jedoch im Gegensatz zu den Strandart Pattern, die
weitgehend von der eingestzten Programmiersprache und der verwendeten Plattform
unabhängig sind, meist Idioms darstellen (also programmiersprache- und oft auch
releaseabhängige Lösungen).
Auch das MVC Muster kann im J2EE implementiert werden:
Die folgenden vier Idioms sind aus dem Buch
"J2EE Patterns" von Adam Bien (ISBN: 3-8273-2124-7)
zusammengetragen und stellen ein Ausschnitt der darin
vorgestellten Pattern dar:
- Service Locator
zur Kapselung der Technologie
- Business Delegate
zur Vereinfachung der Schnittstelle
- Value Object
zur Minimierung der Remote Aufrufe
- Session Fassade
zur Kapselung, Caching, Transaktionssteuerung
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 57 von total 218 Seiten
J2EE – EJB 2.x
9.5.1
Service Locator
Die Suche und Erzeugung von EJB ist zeitintensiv und fehleranfällig aufgrund von
Namensdiensten (JNDI), Komponentenverteilung (RMI-IIOP) und Factorycaching (Home
Interface). Der Service Locator stellt ein Entwurfsmuster zur Kapselung der Technologie und
eine Performancesteigerung zur Verfügung.
Problem:
Bei einem Zugriff auf eine EJB muss zuerst ein Namensdienst initialisiert und die Verbindung
zum Server hergestellt werden. Dazu werden produktspezifische Informationen benötigt. Die
meisten JNDI Implementierungen basieren auf CORBA-Technologie, die eine Interpretation des
Servers verlangt (z.B: "iiop://localhost:3700"). Dies kann bis zu mehreren Sekunden in
Anspruch nehmen, da der Name zuerst von einem DNS aufgelöst werden muss.
Lösung:
Das Ermitteln von Dienstleistungen (z.B. JNDI Context) wird in den Locator verlagert. Dadurch
verschwindet das sich wiederholende Downcasting über die narrow Methode von
PortableRemoteObject im Service Locator.
Die folgenden Anforderungen werden an den Service Locator gestellt:
- bereits instanzierte Instanz des Home Objektes und Ressourcen werden gecached
- Handels (robuste Referenzen) werden gecached
- JNDI Technologie ist transparent für den Client
Client
Applikation Server
EJB
DBMS
DATA
Service Locator
- Zugriff auf JNDI
- CACHE für ServerObjekte
Implementation:
Der Service Locator kapselt die Zugriffe auf das JNDI und stellt ein Cache für die
Serverschnittstellenobjekte zur Verfügung. Der Service Locator wird meist als Singelton
implementiert, damit auch nur tatsächlich eine Instanz des InitialContext pro VM
existiert.
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 58 von total 218 Seiten
J2EE – EJB 2.x
ServiceLocator to cache HomeObject (Ausschnitt)
public EJBHome getHome(String name,boolean cached) throws Exception{
Object temp = null;
if(cached) temp = this.homeCache.get(name);
EJBHome home = null;
if(temp !=null && temp instanceof EJBHome){
return (EJBHome)temp;
}else{
Object ref = context.lookup(name);
EJBMetaData meta = ((EJBHome)ref).getEJBMetaData();
home = (EJBHome) PortableRemoteObject.narrow(
ref,meta.getHomeInterfaceClass());
if(!meta.isSession() || meta.isStatelessSession())
this.homeCache.put(name,home);
}
return home;
}
public static ServiceLocator getInstance() throws Exception{
if(instance==null) instance = new ServiceLocator();
return instance;
}
public EJBHome getHome(String name) throws Exception{
return getHome(name,true);
}
public static void main(String[] args) throws Exception{
Hashtable hash = new Hashtable();
hash.put("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
hash.put("java.naming.provider.url","iiop://localhost:3700");
}
}
ServiceLocatorClient
public class ServiceLocatorClient {
public static void main(String args[]) throws Exception{
HomeFinder finder = ServiceLocator.getInstance();
EJBHome home = finder.getHome("Test");
System.out.println("HomeClass " + home.getClass().getName());
TestHome testHome = (TestHome)home;
Test test = testHome.create();
}
}
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 59 von total 218 Seiten
J2EE – EJB 2.x
9.5.2
Business Delegate
Der Remotezugriff auf einen Server erfordert die Implementation einer entsprechenden Technologie (z.B. RMI-IIOP). Ein Client ist aber nicht an der Technologie sondern vielmehr an der
Geschäftslogik interessiert. Das Idiom des Business Delegate ermöglicht eine Kapselung der
Technologie und somit eine Vereinfachung der Schnittstelle zur Geschäftslogik.
Problem:
Bei einem Zugriff auf die Geschäftslogik in einem EJB-Kontainer ist oft ein Remotezugriff notwendig. Ein Remotezugriff ist jedoch viel komplexer und zeitintensiver als ein Lokalzugriff.
Zusätzlich müssen noch Exceptions (HostNotFoundException,
StubNotFoundException, etc.) abgefangen werden.
Lösung:
Die einzelnen Beans werden über eine zentrale Session Bean angesprochen. Diese hält die
Instanzen der verschiedenen Beans und stellt diese dem Client in einer Schnittstelle zur
Verfügung und kann auch Informationen an die anderen Beans weitergeben. Der Client
produziert "Events" die vom Business Delegate Idiom "verteilt" werden und hat einen einfachen
Zugriff auf die Geschäftslogik zur Verfügung.
Client
Applikation Server
DBMS
EJB
DATA
Business Delegate
- Kapselung der BusinessLogik
Implementation:
Das Business Delegate stellt die Schnittstelle zwischen Client und Geschäftslogik zur Verfügung. Dabei kapselt dieses Idiom die Zugriffslogik. Das Business Delegate wird vom Client
instanziert und benutzt, die notwendigen Services werden durch den Service Locator beschafft.
Business Delegate Interface (wird dem Client zur Verfügung gestellt)
public interface BusinessDelegateIF {
public CustomerVO getCustomer(String id);
public CustomersVO[] getAllCustomerForZip(String zipPrefix);
public CustomersVO[] getAllCustomers();
...
}
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 60 von total 218 Seiten
J2EE – EJB 2.x
Business Delegate (Kapselung der BusinessMethoden)
public CustomerVO getCustomer(String id){
CustomerVO customer = null;
try{
customer = this.customer.findByPrimaryKey(id);
}catch(RemoteException e){
throws new ProblemWithBusinessMethodeException(this.e);
}
return customer:
}
...
9.5.3
Value Object
Die Datenübertragung über das Netzwerk, die meistens von der Data Tier bis zur Client Tier
und umgekehrt erfolgt, ist oft limitierender Faktor in J2EE Applikationen. Das Value Object
Idiom verbessert die Performance und minimiert die Remote Methodenaufrufe.
Problem:
Der Zugriff auf ein EJB erfolgt oft über Remote Methodenaufrufe. Oft wird jedoch nur eine
Teilmenge der benötigten Daten übermittelt (z.B: getter / setter Methoden). Sinnvoller ist es die
gesammte benötigte Datenmenge (Datensatz) in einem Methodenaufruf zu übermitteln.
Enterprise Beans haben in der Regel ganze Gruppen von Attributen, die vom Client in der
Gruppe gebündelt abgerufen und gesetzt werden können. Auf diese Weise muss nicht für jedes
Attribut einzeln eine Methode zum Setzen und Abfragen aufgerufen werden. Der Client erhält
die Attribute in einem Value Object gebündelt.
Lösung:
Die vielen "get"-Aufrufe werden bereits im EJB Container zusammengefasst und dem Client als
Objekt übermittelt. Dieses Kontainer-Objekt besteht nur aus Zugriffslogik und keiner
Geschäftslogik, ist somit ein Value Objekt und entspricht folgenden Anforderungen:
- minimieren der Remote Methodenaufrufe an den Server
- Die Geschäftsdaten werden als Objekt (Value Object) an den Client übergeben
- das Auslesen der Objekt Attribute findet clientseitig statt
Applikation Server
Client
EJB
DBMS
DATA
Business Delegate
ValueObject
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 61 von total 218 Seiten
J2EE – EJB 2.x
Implementation:
Das Value Objekt wird durch eine serialisierbare Klasse repräsentiert, die lediglich zu
Datenhaltungszwecken dient. Das Value Object wird auf der entfernten Maschine erzeugt und
gefüllt, über das Netzwerk transferiert und lokal ausgelesen.
Value Object als serialisierbare Klasse
public class PersonVO implements Serializable{
public String id
= null;
public String fname
= null;
public String name
= null;
public PersonVO(){}
public PersonVO(String id, String fname, String name) {
this.init(id,fname,name);
}
protected void init(String id, String fname, String name);
this.id = id;
this.fname = fname;
this.name = name;
}
public PersonVO(PersonVO person){
this.init(person.getId(),person.getFname(),person.getName());
}
public String getId(){ return this.id; }
public String getFname(){ return this.fname; }
public String getName(){ return this.name; }
public void setId(String id){this.id = id;}
public void setFname(String fname){this.fname = fname;}
public void setName(String name){this.name = name;}
}
Grundsätzlich können Value Objects von folgenden Komponenten erzeugt werden:
-
Data Access Objects
Session Façade (einer Session Bean, die den Zugriff auf ein Entity Bean managed)
Business Delegate
Session Bean
Entity Bean
Komponente der Präsentationsschicht
Es handelt sich also grundsätzlich um Komponenten, die in der Geschäftslogik oder in der
Integrationsschicht liegen und in der Lage sind, benötigte Daten zu beschaffen. Meistens
werden jedoch die Value Objects von den Entity Beans erzeugt, da diese die notwendige
"Persistence Schicht" repräsentieren.
Die CMP Entity Bean der EJB 2.0 ist ein "Value Object"!
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 62 von total 218 Seiten
J2EE – EJB 2.x
Beispiel für eine EJB 2.0 CMP Persistenz:
public abstract class AddressBean implements EntityBean {
public
public
public
public
public
public
abstract
abstract
abstract
abstract
abstract
abstract
String getId();
void setId(String id);
String getStreet();
void setStreet(String street);
String getLocation();
void setLocation(String location);
public String ejbCreate(String street, String location)
throws CreateException {
setId(street + location);
setStreet(street);
setLocation(location);
return null;
}
public AddressVO getAddressVO(){
return new AddressVO(getId(),getStreet(),getLocation());
}
...
}
9.5.4
Session Façade
Die Geschäftslogik liegt typischerweise im EJB Container; diese wird durch Komponenten
(EJBs) abgebildet. Ein Client, der diese Logik verwenden möchte muss den Zusammenhang
der einzelnen Komponenten im Container kennen.
Problem:
Entity Beans bilden persistente Objekte ab, repräsentieren aber lediglich den Zustand der
darunterliegenden Datenbank. Die EJBs sind in Bezug auf Wiederverwendbarkeit,
Transaktionssteuerung sowie Zustandserhaltung nicht, oder nur mit sehr hohem Aufwand
verbunden, vom Client trennbar.
Wenn ein Client auf verschiedene Entity Beans zugreift und verschiedene Methoden der Entity
Beans aufruft, um eine Aufgabe zu erledigen, kann dies verschiedene Probleme mit sich
bringen. Zum einen erhöht sich der Netzwerk Traffic, des weiteren wird die Workflow Logik auf
den Client verlagert, zum anderen sind die Methodenaufrufe durch die client-seitige
Inszenierung möglicherweise nicht in einen notwendigen Transaktionszusammenhang gestellt.
Die Entity Beans mit weiterer Processing Logik aufzublähen, ist nicht der richtige
Lösungsansatz. Da die Entity Bean Schicht von der langfristigen Perspektive her aus
verschiedenen Sichten und Aufgaben gesehen wird, wird der Einbau von Anwendungslogik zu
Zwecken der Performance Verbesserung eher zu Wartungsproblemen führen.
Deshalb sollte die Anwendungslogik, die eine Kette von Aufrufen von Entity Bean Methoden
widerspiegelt, in einer Session Bean implementiert werden. Der Client ruft dann eine Methode
der Session Bean auf, um seine Aufgabe zu erledigen. Die Methode der Session Bean
übernimmt die Aufrufe der Methoden der verschiedenen Entity Beans.
Lösung:
Was der Client möchte ist eine Schnittstelle, die ein Use Case abbildet. Dem Client kann es
egal sein, wieviele Entity oder Session Beans diese Schnittstelle implementiert. Für den Client
ist nur die korrekte Abbildung des Anwendungsfalles wichtig. Das Session Façade definiert eine
neue "Schicht", die als Session Bean implementiert ist und folgende Anfgorderungen erfüllt:
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 63 von total 218 Seiten
J2EE – EJB 2.x
-
Abschirmung und Entkoppelung des Client vor Subsystemen ( Entity Bean, etc. )
Erhalt der Unabhängigkeit der Subsysteme
Vereinfachung der Business Schnittstelle
Implementierung von allgemeinen Diensten wie Caching, Authorisierung, etc.
Zustandsverwaltung des Client
Transaktionssteuerung
Client
Applikation Server
DBMS
EJB
DATA
EJB
Session Façade
Implementation:
In einer zusätzlichen Schicht, implementiert mit einer Session Bean, werden diese Anforderungen untergebracht. Falls ein Caching gefordert ist, muss die Session Bean statefull
sein. Diese EJB entspricht in ihrer Funktionalität dem klassischen GoF Façade Pattern. Sie
bietet eine einfache Schnittstelle und verbirgt die Komplexität der Subsysteme vor dem Client.
Session Façade um einen Datensatz zu ändern; eine Transaktion wird gestartet (Ausschnitt)
public void updatePerson (PersonVO person) throws Exception{
try{
getPerson(person.getId()).remove();
this.createPerson(person);
}catch(Exception e){
throw new Exception("PersonBean.updatePerson " + e);
}
}
9.6
Weitere Pattern im J2EE Umfeld
9.6.1
Value Object Assembler
Der Value Object Assembler stellt Value Objects und Schichten dynamisch in Session Beans
zusammen und kommuniziert diese an den Presentation Layer. Er reichert Value Objects um
Attribute an, die nur in der Bearbeitungsschicht benötigt werden, aber nicht in den Business
Objects.
9.6.2
Composite Entity
Business Objekte sind oft nicht einfache Strukturen, sondern Graphen von Attribut Knoten. Ein
Beleg hat Belegzeilen, und jede Belegzeile kann wieder mehrere Kostenrechnungsinformationen enthalten. Um der Modellschicht eine grössere Konsistenz zu geben, ist es
besser, wenn für die abhängigen Objekte eine zentrale Entity Bean geschaffen wird, über die
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 64 von total 218 Seiten
J2EE – EJB 2.x
eine Session Bean mit den abhängigen Objekten umgeht. Das zentrale Business Objekt steuert
seine abhängigen Objekte – die meist selbst wieder Entity Beans sind.
9.6.3
Data Access Objects DAO
Das Data Access Object Pattern trennt das Interface für eine System Ressource von der
tatsächlichen Zugriffsstrategie. Jede Enterprise Bean, die auf eine Backend Ressource wie
eine Datenbanktabelle zugreift, kann/sollte eine zugehörige DAO Klasse für diese Ressource
haben.
Dass das Laden und Speichern über JDBC und SQL geschieht, bleibt für die Anwendungsschicht verborgen. Wenn ein anderer Persistenz Mechanismus gewählt wird, kann der Code im
DAO ausgetauscht werden, ohne dass die restliche Anwendung davon betroffen ist.
9.6.4
Core J2EE Patterns
http://java.sun.com/blueprints/corej2eepatterns/Patterns/index.html
© 2008 Stephan Metzler
Design Patterns
Version 3.0
Seite 65 von total 218 Seiten
J2EE – EJB 2.x
10
Programmiereinschränkungen (Regeln)
1
2
3
4
5
kein Threads Management oder Synchronisation in (EJB) Beans
- ist Aufgabe des Applikationsservers
- Bean = einzelner Thread, keine Kongruenz
keine Klassenvariablen / -methoden verwenden
- verschiedene Prozesse bei Cluster-Deployment
kein stdin / -out verwenden
- Ausgabe- / Eingabestream sind nicht definiert
kein direkter Zugriff auf Dateien oder Directories
- nicht definierter Speicherort bei Cluster-Deployment
keine native Libraries
- nicht definierter Speicherort bei Cluster-Deployment
keine this – Referenz
6
© 2008 Stephan Metzler
Programmiereinschränkungen (Regeln)
- SessionContext.getEJBObject();
- EntityContext.getEJBObject();
Der EJB Context javax.ejb.SessionContext und
javax.ejb.EntityContext erlaubt den Zugriff auf das
REMOTE Interface des aktuellen Beans. Dies ergibt eine
Referenz auf das Bean selbst, die weitergegeben werden kann.
Version 3.0
Seite 66 von total 218 Seiten
J2EE – EJB 2.x
11
EJB Anwendungsbeispiele mit JBoss AS
JBoss™ als Open Source Applikationsserver hat einen potentiellen Markt im J2EE Umfeld. Für
das Testen und Kennenlernen von J2EE Applikationen bietet JBoss zusammen mit dem Eclipse
Entwicklungstool eine ready-to-use Plattform.
Entsprechende Installationsinformationen sind im Anhang aufgeführt. Die nachfolgenden
Erläuterungen und Beispiele dienen zur Repetition der behandelten EJB Theorie und geben
einen Einblick in das Aufsetzen von J2EE Applikationen mit dem JBoss AS
(http.//www.jboss.org)
11.1
wiederverwendbare Komponenten
EJBs sind als wiederverwendbare, verteilte Komponenten konzipiert, welche relativ lose
gekoppelt und in einer verteilten Umgebung einsetzbar sind.
Der Verteilungsaspekt dieser Komponenten ermöglicht die Trennung der Anwendung
(Geschäftslogik), der Bedienerführung (Darstellungslogik) und der Datenerhaltung (Persistenz),
denn ohne dem Deployen dieser Komponenten :
- kann jeder Fehler in irgendeinem der Client-Programme den Datenbankinhalt beschädigen
- kann ein Benutzer versuchen, die Geschäftslogik zu umgehen und direkt auf die Datenbank
zuzugreifen
- muss eine neue Version der Geschäftslogik auf allen PCs installiert werden
- wird die Modularisierung nicht gefördert
- ist die Art der Darstellung ebenso festgelegt wie die Art der Verarbeitung
Daher trennt man die Geschäftslogik von der Darstellung und installiert sie auf einem zentralen
Server, der zwischen den Clients und dem Datenbankserver vermittelt. Das ist die klassische 3Schicht-Architektur (3-Tier Architecture).
EJB bilden Suns Ansatz, um die gewünschte Flexibilität zu erreichen. Die mittlere Schicht wird
in Komponenten zerlegt, was auch die Änder- und Erweiterbarkeit unterstützt (und der
Wiederverwendung, um den Aufwand zur Erstellung zu kompensieren). Zur Kommunikation
zwischen den Komponenten dient gebräuchliche Middleware, vor allem RMI, aber auch (zum
Client) CORBA oder Webdienste.
11.2
EJB Container
Ein EJB-Container in Verbindung mit einem Application Server nimmt den Programmierern
erhebliche Arbeit ab:
- die Verteilung der Objekte:
- die Anbindung eines Webservers (lokal oder über RMI-IIOP), der die Kommunikation mit
den Clients übernimmt
- die Anbindung der Clients oder anderer Enterprise Beans über RMI-IIOP
- den lokalen Zugriff auf andere Enterprise Beans
- die Anbindung von Clients oder anderen Anwendungen über SOAP (und WSDL)
- den Datenbankzugriff (über JDBC)
- die Thread-Verwaltung und Synchronisation - EJBs müssen nicht thread-sicher sein
- die Prozessverwaltung um die Last optimal bewältigen zu können
- Zuverlässigkeit und eventuell sogar Lastverteilung auf mehrere Server
© 2008 Stephan Metzler
EJB Anwendungsbeispiele mit JBoss AS
Version 3.0
Seite 67 von total 218 Seiten
J2EE – EJB 2.x
-
11.3
Zustandsverwaltung - Objekte werden bei Bedarf aktiviert und deaktiviert
Pooling von z.B. Datenbankverbindungen
Transaktionen und Wiederherstellung
auf Wunsch den Datenaustausch mit der Datenbank ohne (sichtbares) SQL
Sicherheit
Werkzeuge zur Systemadministration
EJB Ausprägungen
Entity Beans
-
besitzen einen dauerhaften (persistenten) Zustand
modellieren Objekte im Anwendungsbereich
stellen Datenbehälter mit Regeln zur Sicherung eines konsistenten Zustands dar
bilden Datensätze in einer Datenbank-Tabelle ab
SessionSession-Bean
- zustandsbehaftet, werden sie bei Bedarf erzeugt und sind einem bestimmten Client
zugeordnet, dessen Sitzung sie verwalten, ihr Zustand wird nicht gespeichert und geht nach
Gebrauch verloren
- zustandslos, verwalten sie Sitzungen, sind aber austauschbar und können daher in einem
Pool gehalten werden. Das macht sie wesentlich leistungsfähiger als die Beans mit Zustand.
Sie können eine Webdienst-Schnittstelle haben (neu in EJB 2.1)
- modellieren Geschäftsvorgänge (Tätigkeiten, Anwendungsfälle) im Anwendungsbereich
- dient zur Kommunikation mit dem Client
- nehmen oft die Dienste von Entity-Beans und anderen Session-Beans in Anspruch
MessageDrivenMessageDriven-Bean (ab EJB 2.0)
- sind zustandslos
- dienen zur asynchronen Kommunikation, d.h. für Anfragen, auf die keine unmittelbare
Antwort erwartet wird
- sind nicht über eine Schnittstelle, sondern über eine Topic- oder Queue-Destination
ansprechbar
- werden durch ein Ereignis aktiviert
- Verwendung gleicht der von zustandslosen Session-Beans
© 2008 Stephan Metzler
EJB Anwendungsbeispiele mit JBoss AS
Version 3.0
Seite 68 von total 218 Seiten
J2EE – EJB 2.x
11.4
EJB Schnittstellen
Jede Enterprise Bean kann eine oder mehrere der folgenden Schnittstellen implementieren:
- remote Interfaces erlauben den Zugriff über RMI-IIOP, sei es von aussen oder lokal
- local Interfaces (neu in EJB 2.0) erlauben den Zugriff nur innerhalb des gleichen Application
Servers (in den auch ein Webserver integriert sein kann). Diese Zugriffe sind effizienter und
haben die normale Java-Semantik und die gleichen Parameter wie Remote Interfaces
Sowohl von local wie auch von remote Schnittstellen gibt es je zwei Ausprägungen:
- Remote Interface, enthält die Methoden der Anwendung
- Home Interface, dient zur Verwaltung der Enterprise Bean
- Webdienst-Schnittstellen für die Stateless Session-Bean (neu in EJB 2.1), dabei ist nur die
Remote Schnittstelle implementiert
11.5
EJB Bestandteile
Eine Enterprise Bean besteht aus mehreren Teilen:
BeanBean-Klasse
class AnwendungBean implements EntityBean/SessionBean/MessageDrivenBean {
...
}
Interfaces
interface Anwendung extends EJBLocalObject/EJBObject/Remote {
...
}
interface AnwendungHome extends EJBLocalHome/EJBHome {
...
}
DeploymentDeployment-Descriptor
...
<enterprise-beans>
<entity>
<ejb-name>Anwendung1EJB</ejb-name>
<home>Anwendung1Home</home>
<remote>Anwendung1</remote>
<ejb-class>Anwendung1Bean</ejb-class>
...
</entity>
<session>
<ejb-name>Anwendung2EJB</ejb-name>
<home>Anwendung2Home</home>
<remote>Anwendung2</remote>
<ejb-class>Anwendung2Bean</ejb-class>
...
</session>
...
</enterprise-beans>
© 2008 Stephan Metzler
EJB Anwendungsbeispiele mit JBoss AS
Version 3.0
Seite 69 von total 218 Seiten
J2EE – EJB 2.x
Verpackt in eine jarjar-Datei (EJB 2.0)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD
EnterpriseJavaBeans 2.0//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd">
<ejb-jar>
...
</ejb-jar>
Verpackt in eine jarjar-Datei (EJB 2.1)
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd"
version="2.1">
...
</ejb-jar>
Zwischen den Versionen ist man von DTDs auf XML-Schema umgestiegen.
Die Schnittstellen und der Deployment-Descriptor können von Werkzeugen aus der BeanKlasse erzeugt werden, etwa durch XDoclet (http://sourceforge.net/projects/xdoclet). Dabei
werden die entsprechenden javadoc-Kommentare ausgewertet. Den Deployment-Descriptor
erfassen viele Application Server auch per Formular.
11.6
EJB Implementation
- die Bean-Klasse muss einen parameterlosen Konstruktor besitzen
- die Schnittstelle EnterpriseBean erweitert die Schnittstelle Serializable, damit handelt es sich
um eine echte Bean
- die Bean-Klasse muss die Methoden aus allen im Deployment-Descriptor angesprochenen
Schnittstellen implementieren - allerdings nicht unmittelbar. Diese Schnittstellen zeigen an,
was man von ausserhalb des Application Servers sieht. Beim Deployment werden Klassen
erzeugt, die sie implementieren. Die Bean-Klasse muss entsprechende Methoden
bereitstellen, die die eigentlichen Aktionen enthalten:
- bei Entity-Beans müssen zu jeder createXYZ-Methode aus dem Home-Interface zwei
Methoden ejbCreateXYZ und ejbPostCreateXYZ mit der gleichen Signatur implementiert
werden
- bei Session-Beans muss zu jeder createXYZ-Methode aus dem Home-Interface die
Methoden ejbCreateXYZ mit der gleichen Signatur implementiert werden
- bei MessageDriven-Beans muss eine Methode ejbCreate implementiert werden
- bei Entity-Beans muss zu jeder findXYZ-Methode aus dem Home-Interfcae eine Methode
ejbFindXYZ mit der gleichen Signatur implementiert werden
- bei Entity-Beans muss zu jeder Methode XYZ des Home-Interface eine Methode
ejbHomeXYZ mit der gleichen Signatur implementiert werden
- zusätzlich muss jede Methode der Anwendung aus dem Remote-Interace mit der gleichen
Signatur implementiert werden
© 2008 Stephan Metzler
EJB Anwendungsbeispiele mit JBoss AS
Version 3.0
Seite 70 von total 218 Seiten
J2EE – EJB 2.x
11.7
EJB Deployment Descriptors
Die für den Deployprozess notwendigen Deployment Descriptoren, die wir bis anhin mit dem
Deploytool erstellt haben möchten wir näher betrachten:
-
ejb-jar.xml
jboss.xml
application.xml
application-client.xml
jboss-client.jar
jbosscmp-jdbc.xml
ejbejb-jar.xml : beschreibt Bean auf logischer Ebene
Namen der Java-Klassen
Namen der Attribute
Transaktionsverhalten
Security Mapping
- Spezifikation der Query-Methoden
- Resource Referenzen
-
ejb-jar.xml ENC Elements
(ENC = Enterprise Naming Context)
jboss.xml : bescheibt Informationen für den
Application Server
- JNDI-Mapping: Name unter dem das HomeInterface gefunden wird
- Resource Referenzen
jboss.xml ENC Elements
(ENC = Enterprise Naming Context)
<jboss>
<enterprise-beans>
<session>
<ejb-name>moneyexchange</ejb-name>
<jndi-name>ejb/moneyexchange</jndi-name>
<resource-ref>
<res-ref-name>jdbc/ExchangeTable</res-ref-name>
<jndi-name>java:/mySQLDS</jndi-name>
</resource-ref>
</session>
</enterprise-beans>
</jboss>
© 2008 Stephan Metzler
EJB Anwendungsbeispiele mit JBoss AS
Version 3.0
Seite 71 von total 218 Seiten
J2EE – EJB 2.x
application.xml : beschreibt eine ganze Enterprise Applikation
Die graphische Representation zeigt die Struktur des J2EE application.XML Schema.
<application>
<display-name>
MoneyExchange
</display-name>
<module>
<ejb>
MoneyExchange.jar
</ejb>
</module>
</application>
applicationapplication-client.xml : beschreibt
First Tier Client
Client Program
Alle gültigen application-client.xml Deployment Descriptoren müssen dem abgebildeten XML
Schema oder einer entsprechenden DTD Definition folgen und beschreiben:
-
Client Beschreibung
Environment Entries
EJB Referenzen
Resource Referenzen
Resource Environment Referenzen
<application-client>
<display-name>
MoneyExchangeClient
</display-name>
<ejb-ref>
<ejb-ref-name>
ejb/MoneyExchange
</ejb-ref-name>
<ejb-ref-type>
Session
</ejb-ref-type>
<home>
beans.MoneyExchangeHome
</home>
<remote>
beans.MoneyExchange
</remote>
</ejb-ref>
</application-client>
© 2008 Stephan Metzler
EJB Anwendungsbeispiele mit JBoss AS
Version 3.0
Seite 72 von total 218 Seiten
J2EE – EJB 2.x
jbossjboss-client.xml : bescheibt Informationen für den Application Server
- Versionsnummern,....
- JNDI-Mapping: Name unter dem das Home-Interface gefunden wird
- Resource Referenzen
<jboss-client>
<jndi-name>MoneyExchangeClient</jndi-name>
<ejb-ref>
<ejb-ref-name>ejb/MoneyExchange</ejb-ref-name>
<jndi-name>moneyexchange</jndi-name>
</ejb-ref>
</jboss-client>
jbosscmpjbosscmp-jdbc.xml : beschreibt O/RO/R-Mapping für
für die Datenbank (applikationsserverspezifisch)
Datasource: Information für den DB-Connect im
Application Server
- jndi-Name: Name unter dem das
Home-Interface gefunden wird
<jbosscmp-jdbc>
<defaults>
<datasource>
java:/DefaultDS
</datasource>
<datasource-mapping>
MySQL
</datasource-mapping>
<create-table>true</create-table>
<remove-table>true</remove-table>
</defaults>
<enterprise-beans>
<entity>
<ejb-name>Product</ejb-name>
<table-name>ProductTable</table-name>
</entity>
</enterprise-beans>
</jbosscmp-jdbc>
© 2008 Stephan Metzler
EJB Anwendungsbeispiele mit JBoss AS
Version 3.0
Seite 73 von total 218 Seiten
J2EE – EJB 2.x
12
Übung ee4 : Datenanbindung mit Stateless Session Bean
In diesem Beispiel soll eine Datenquelle von einer Session Bean aus angesprochen werden.
Während der erste Lösungsansatz einer standart JDBC Verbindung beschreibt, konzentriert
sich der zweite Lösungsansatz auf den Lookup des Datasource Objektes per JNDI.
12.1
JDBC API
Klassen und
Schnittstellen der:
java.sql
JDBC API
12.2
Währungsrechner mit Session-Beans
Erstellen Sie ein Stateless Session Bean zum Umrechnen von Währungskursen ( z.B. CHF >
EUR), die Währungskurse sollen durch eine DB-Tabelle persistent gehalten werden und durch
das EJB repräsentiert werden.
Client Attribute :
final int amount = 250;
final String changeTo = "EUR";
Die Basiswährung ist "CHF"
12.2.1
Datenbankanbindung mit Session-Beans
Lesen Sie den Umrechnungskurs aus der
mySQL Datenbank "ee" und erweitern Sie Ihre
Applikation mit mehreren möglichen
Währungseinheiten, z.B. USD, GBP, etc.
Erstellen Sie eine entsprechende Tabelle "rates" in der mySQL DB und definieren Sie die
Kurse:
CREATE TABLE RATES(
ID VARCHAR(10) PRIMARY KEY,
BASECUR VARCHAR(3),
RATE VARCHAR(10)
);
INSERT INTO RATES VALUES('1', 'USD', '1.25');
INSERT INTO RATES VALUES('2', 'EUR', '1.52');
INSERT INTO RATES VALUES('3', 'CHF', '1.00');
Alternative: Benutzen Sie das entsprechende Jakarta-Ant Target "run-sql-script" das in
der build.xml Datei vorbereitet ist.
Für die Datenbank Anbindung fokussieren wir zwei Varianten: "JDBC" und "JNDI Lookup"
© 2008 Stephan Metzler
Übung ee4 : Datenanbindung mit Stateless Session Bean
Version 3.0
Seite 74 von total 218 Seiten
J2EE – EJB 2.x
12.2.2
JDBC Connection
Ergänzen Sie den Source der Bean mit den notwendigen Statements um die JDBC
Datenquelle localhost:ExchangeTable.rates abzufragen und übergeben Sie die
Währungseinheit als Parameter der Business-Methode :
Source Code
Code Ansatz: JDBC Connection in MoneyExchangeBean.java
String SQLQuery =
final String user
final String pass
final String url
"SELECT rate FROM rates WHERE ...
= "...";
= "...";
= "jdbc:mysql://localhost/ee";
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection(url, user, pass);
Statement stm = con.createStatement();
Resultset rs = stm.executeQuery(SQLQuery);
12.2.3
JNDI lookup Connection
Benutzen Sie den Namingservice um über einen lookup die Datenquelle an das Bean
anzubinden. Der für den JNDI lookup massgebende Name wird im Deployment Descriptor
definiert:
ejbejb-jar.xml (Ausschnitt)
<resource-ref>
<res-ref-name>jdbc/ExchangeTable</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
jboss.xml (Ausschnitt)
<resource-ref>
<res-ref-name>jdbc/ExchangeTable</res-ref-name>
<jndi-name>java:/mySQLDS</jndi-name>
</resource-ref>
[JBoss_Home]/server/ee/deploy/mysql[JBoss_Home]/server/ee/deploy/mysql-ds.xml (Ausschnitt)
<datasources>
<local-tx-datasource>
<jndi-name>mySQLDS</jndi-name>
<connection-url>
jdbc:mysql://localhost:3306/ee
</connection-url>
Source Code Ansatz: JNDI lookup Datenbank Connection in MoneyExchangeBean.java
InitialContext ic = new InitialContext();
DataSource ds = (DataSource)
ic.lookup("java:comp/env/jdbc/ExchangeTable");
© 2008 Stephan Metzler
Übung ee4 : Datenanbindung mit Stateless Session Bean
Version 3.0
Seite 75 von total 218 Seiten
J2EE – EJB 2.x
13
Übung ee5 : Robuste Referenzen auf Stateful Session Beans
13.1
Handles
Handles definieren robuste Referenzen auf Instanzen, die man zwischen Maschinen
(netzwerkfähig) austauschen kann :
persistent (dauerhaft)
transferable (verschiebbar)
serializable (in nachfolgende Packete zerlegbar)
provided by the container (vom Kontainer des Applikationservers zur Verfügung gestellt)
consists of just one method, that returns the Home / Remote Interface
(enthält nur eine einzige Methode mit dem Home / Remote Interface als Returnwert)
- guaranteed to work across client JVM's and server (Plattformunabhängig)
-
Class Definition
package javax.ejb;
public interface Handle extends Serializable {
public EJBObject getEJBObject() throws RemoteException;
}
package javax.ejb;
public interface HomeHandle extends Serializable {
public EJBHome getEJBHome() throws RemoteException;
}
Creating / Storing a Handle am Beispiel eines HomeHandle
Home home = ...
HomeHandle home_handle = home.getHomeHandle();
ObjectOutputStream oos = new ObjectOutptStream( new
FileOutputStream ( new File("myHomeHandle.ser")));
oos.writeObject(home_handle);
oos.close();
Using a Handle am Beispiel eines HomeHandle
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("myHomeHandle.ser"));
ois.close();
HomeHandle home_handle = (HomeHandle)ois.readObject();
Object obj = home_handle.getEJBHome();
Home home = (Home)PortableRemoteObject.narrow( obj, Home.class);
Für den Handle des Remote Objectes ist die Anwendung analog, anstelle des HomeHandle tritt
das Object Handle. Ob der Handle oder HomeHandle für späteren Beanzugriff persistiert wird
ist von der Applikations und der Zugriffslogik abhängig.
© 2008 Stephan Metzler
Übung ee5 : Robuste Referenzen auf Stateful Session Beans
Version 3.0
Seite 76 von total 218 Seiten
J2EE – EJB 2.x
13.2
Kontoführung mit Stateful Session Bean
Entwickeln Sie eine triviale Kontoführung realisiert mit einer Stateful Session Bean. Um
mehrere Konten parallel führen zu können sollen die entsprechenden Handels der EJBReferenzen dienen.
Führen Sie für mehrere "Kunden" individuelle Konten, die es erlauben einen :
- Betrag zu deponieren (als Float)
- Betrag abzuheben (als Float)
- Saldo auszugeben (auf der Konsole)
1. Deklarieren Sie die verschiedenen Business-Methoden im Remote Interface und definieren
Sie diese in der Bean Klasse.
2. Konzipieren Sie ein Client-Testprogramm um Ihre Applikation zu testen.
3. Ergänzen Sie die Bean Methoden mit Konsolen Statements System.out.println zur
Überwachung der Aktivitäten des AS. Überprüfen Sie die entsprechende LOG-Datei.
4. Definieren Sie einen Zinssatz für alle Ihre "Kunden". Der Zinssatz ist für alle Konten gleich.
Suchen Sie nach einer geeigneten Möglichkeit um den Zinssatz ändern zu können.
Implementieren und Überprüfen Sie Ihre Wahl.
13.2.1
Bean Identität mit Handle speichern
Um in der Client-Applikation mehrere Konten parallel zu führen kann die getHandle Methode
benützt werden.
Definieren Sie individuelle Konten der Klasse Handle:
private static Handle handle_1;
private static Handle handle_2;
private static Handle handle_3;
Erzeugen Sie das Home Objekt:
Objekt:
private static AutomaticTellerHome erzeugeHomeObjekt()
throws NamingException {
InitialContext ctx = new InitialContext(getInitialProperties());
Object ref = ctx.lookup("account");
home = (AutomaticTellerHome) PortableRemoteObject.narrow(
ref,AutomaticTellerHome.class);
return home;
}
Erzeugen Sie für jeden Account eine eigene Bean-Instanz mit der create Methode. Benützen
Sie die getHandle Methode des Remote Interfaces um die Bean Instanzen zu "sichern":
BeanBean-Insanz:
public static Handle erstelleAccount(AutomaticTellerHome home)
throws CreateException, RemoteException {
AutomaticTeller at = home.create();
return at.getHandle();
}
© 2008 Stephan Metzler
Übung ee5 : Robuste Referenzen auf Stateful Session Beans
Version 3.0
Seite 77 von total 218 Seiten
J2EE – EJB 2.x
Um die Business-Methoden auf den individuellen Accounts ausführen zu können, brauchen Sie
den richtigen "Handle"; diesen bekommen Sie, indem Sie auf dem entsprechenden Handle
Objekt die getEJBObjekt Methode aufrufen.
Erstellen Sie ein Methode getRemoteObjekt, die das für Sie erledigt.
getRemoteObjekt
private static AutomaticTeller getRemoteObjekt(Handle handle)
throws RemoteException {
return (AutomaticTeller) javax.rmi.PortableRemoteObject
.narrow(handle.getEJBObject(), AutomaticTeller.class);
}
Testen Sie nun Ihre "Bank", indem Sie auf den bereitgestellten Konten individuelle Beträge
buchen und abheben.
13.2.2
Handle persistent in Datei speichern
Speichern Sie die Handle mit Hilfe eines FileOutputStream Objektes in eine Datei und
lesen Sie diese in einer anderen Client-Applikation wieder aus. Testen Sie Ihre Applikation.
© 2008 Stephan Metzler
Übung ee5 : Robuste Referenzen auf Stateful Session Beans
Version 3.0
Seite 78 von total 218 Seiten
J2EE – EJB 2.x
14
Übung ee6 : EJB QL (CMP 2.0)
Um die Funktionsweise der Suchmethoden des Home-Inteface zu erläutern ist eine kurze
Betrachtung der EJB Querry Language erforderlich.
14.1
EJB QL
findXYZ:
Bei CMP muss die Methode findByPrimaryKey immer deklariert werden, jedoch wird sie durch
die Bean-Klasse nicht implementiert. Weitere Methoden findBy... (das By ist eine
Namenskonvention) dürfen deklariert werden und werden ebenfalls nicht implementiert. Ihr
Ergebnistyp ist das (local oder remote) Remote-Interface oder eine java.util.Collection davon.
public Name findByPrimaryKey(String id) throws FinderException,
RemoteException;
public Collection findByName(String name) throws FinderException,
RemoteException;
Zu jeder findBy...-Methode (ausgenommen findByPrimaryKey) muss es einen entsprechenden
Eintrag im Deployment-Descriptor geben:
ejbejb-jar.xml (Ausschnitt Finder QL)
<entity>
...
<persistence-type>Container</persistence-type>
...
<abstract-schema-name>...</abstract-schema-name>
...
<primkey-field>...</primkey-field>
<query>
<query-method>
<method-name>findBy...</method-name>
<method-params>
<method-param>Type</method-param>
...
</method-params>
</query-method>
<ejb-ql>
SELECT ... FROM ... WHERE ...
</ejb-ql>
</query>
...
</entity>
Zu jedem Parameter wird die entsprechende Java-Klasse/Schnittstelle angegeben. Wenn es
keine Parameter gibt, gibt es keine method-param Tags.
Der Eintrag (Tag) method-params bleibt jedoch erhalten.
© 2008 Stephan Metzler
Übung ee6 : EJB QL (CMP 2.0)
Version 3.0
Seite 79 von total 218 Seiten
J2EE – EJB 2.x
selectXYZ:
Solche Methoden sind nur bei CMP zulässig. Sie werden nicht deklariert (der Name selectXYZ
taucht auch nirgendwo auf) und durch abstrakte Methoden in der Entity-Bean implementiert:
CMP Bean Klasse
public abstract AnwendungBean implements EntityBean {
...
public abstract Type ejbSelectXYZ(...) throws FinderException;
...
}
Type ist hier deutlich flexibler als bei den findXYZ-Methoden: Neben dem Remote-Interface ist
jeder beliebige (serialisierbare) Java-Typ zulässig, so dass man auch Attributwerte zurückliefern
kann, und neben java.util.Collection ist auch java.util.Set möglich. Die ejbSelectXYZ-Methoden
ihrerseits werden durch einen Eintrag query im Deployment-Descriptor beschrieben, genau wie
die findXYZ-Methoden.
Die EJB QL selbst ist eine kleine Untermenge von SQL. Eine Abfrage SELECT ... FROM ...
WHERE ... besteht aus folgenden Teilen:
SELECT::
Optional darf DISTINCT folgen. Hier steht ein Ausdruck, der keine Operatoren enthalten darf.
Abgesehen von Aggregatfunktionen (seit EJB 2.1) COUNT, SUM, AVG, MIN, MAX besteht der
Ausdruck aus einem Pfad der Form a, b, c, der einen einzelnen Wert liefert und dessen
Bestandteile einzelne Objekte sind. Steht hier eine einzelne Tabelle t aus der FROM-Klausel,
muss OBJECT(t) resultieren.
FROM::
Hier steht eine (durch Komma getrennte) Liste von Tabellen (bzw. abstrakten Schemata) der
Form Table AS t und Aufzählungen der Form IN (Collection) AS c. Das
Schlüsselwort AS darf entfallen. Collection darf ein Pfad wie bei SELECT sein, der jetzt
aber keinen einzelnen Wert liefern darf. Die eingeführten Bezeichner (hier t bzw. c) werden in
nachfolgenden Listenelementen bei FROM sowie bei SELECT und WHERE verwendet.
WHERE (optional):
Hier steht eine Bedingung mit den üblichen Operatoren NOT, AND, OR; =, <>, <, <=, >, >=
(Vorsicht mit XML!), [NOT] BETWEEN, [NOT] IN, [NOT] LIKE, IS [NOT] NULL, IS
[NOT] EMPTY, [NOT] MEMBER OF (IS EMPTY und MEMBER OF fragen Collections ab);
+, -, *, /. Zahlen, Zeichenketten (in einfachen Anführungszeichen) und Mengen (in runden
Klammern) können verwendet werden. An Funktionen gibt es CONCAT, LENGTH, LOCATE,
SUBSTRING, ABS, SQRT und MOD. Die Parameter einer findXYZ- oder ejbSelectXYZMethode werden durch ?1, ?2, ... angesprochen.
© 2008 Stephan Metzler
Übung ee6 : EJB QL (CMP 2.0)
Version 3.0
Seite 80 von total 218 Seiten
J2EE – EJB 2.x
ORDER BY (optional):
(seit EJB 2.1) Hier steht ein Pfadausdruck der Form a, b, c, der direkt vom SELECTAusdruck abhängen muss, gefolgt von ASC (Voreinstellung) oder DESC.
Beispiel
public Collection<Customer> findAll() throws RemoteException,FinderException;
public Collection<Customer> findByComputerCount(int computerCount)
throws RemoteException, FinderException;
resultiert in:
SELECT OBJECT (c) FROM Customer c
SELECT OBJECT (c) FROM Customer c WHERE c.computerCount >= ?1
14.2
Primary Key Klasse
Die Primary Key Klasse ist nur für Entity Beans notwendig. Sie kapselt die Felder, die den
Primary Key der Datenbank repräsentieren in einem eigenen Objekt. Ist der Primary Key in der
Datenbank durch ein einzelnes Feld mit einem Basic Datentyp konzipiert, so darf der Primary
Key im Bean als Standard Java Klasse (z.B: java.lang.Integer oder
java.lang.String) definiert sein. Natürlich müssen die Datenfelder typengleich sein.
(Primitive Datentypen wie int, float oder boolean sind nicht möglich). Dabei wird die Klasse des
Primary Keys und bei CMP das entsprechende Feld im Deployment Descriptor definiert:
<prim-key-class>java.lang.Integer</prim-key-class>
<primkey-field>id</primkey-field>
Die Alternative ist eine eigene PrimaryKeyClass zu definieren. Die Klasse muss serializable sein
und Implementationen der Methoden hashcode() and equals(Object)definieren.
Für CMP sind die folgenden Punkte massgebend:
- Die Felder der Primary Key Klasse müssen als public deklariert werden.
- Die Primary Key Klasse muss einen Default Konstruktor haben.
- Die Namen der Felder der Primary Key Klasse müssen Teil der CMP Felder der Bean
Klasse sein.
Beispiel einer PrimaryKeyKlasse
PrimaryKeyKlasse
public class PersonBeanPK implements java.io.Serializable {
public int id;
public PersonBeanPK(int id) { this.id = id; }
public PersonBeanPK() { }
public int hashcode() { return id; }
public boolean equals(Object other) {
...
}
© 2008 Stephan Metzler
Übung ee6 : EJB QL (CMP 2.0)
Version 3.0
Seite 81 von total 218 Seiten
J2EE – EJB 2.x
14.3
ProductShop
Entwickeln und Testen Sie einen ProductShop mit einer CMP 2.0 Entity Bean, die auf eine
mySQL DB mit den Feldern "productID" und "name" und "price" zugreift. Realisieren Sie eine
PrimaryKeyKlasse, die das Datenbankfeld "productID" repräsentiert.
Definieren Sie entsprechende Finder Methoden wie:
- findByName
- findAllProducts
Ansatz:
findByName Methode im ProductHome Interface:
public Collection findByName(String name) throws FinderException,
RemoteException;
entsprechendes <query> tag im Deployment Descriptor ejb-jar.xml
<query>
<query-method>
<method-name>findByName</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<ejb-ql> SELECT OBJECT(a) FROM ProductBean As a WHERE a.name = ?1</ejb-ql>
</query>
Erstellen einer XML Datei (Deployment Descriptor), mit der Sie das Datenbankmapping
definieren. Diese Datei ergänzt die Einstellungen der Datei
standardjbosscmp-jdbc.xml im \conf Verzeichnis des aktuellen Server:
jbosscmpjbosscmp-jdbc.xml
<jbosscmp-jdbc>
<defaults>
<datasource>java:/DefaultDS</datasource>
<datasource-mapping>mySQL</datasource-mapping>
<create-table>true</create-table>
<remove-table>true</remove-table>
</defaults>
<enterprise-beans>
<entity>
<ejb-name>Product</ejb-name>
<table-name>ProductTable</table-name>
</entity>
</enterprise-beans>
</jbosscmp-jdbc>
© 2008 Stephan Metzler
Übung ee6 : EJB QL (CMP 2.0)
Version 3.0
Seite 82 von total 218 Seiten
J2EE – EJB 2.x
14.3.1
Summe aus allen Produkten
Soll das Total des "Warenkorbes" bestimmt werden, so können mit der getPrice Methode
des Remote Interfaces die Preise mittels Iteration summiert werden:
Summe aller Produkte
double sum = 0;
Iterator i = home.findAllProducts().iterator();
while (i.hasNext()) {
Product prod = (Product)
PortableRemoteObject.narrow(i.next(),Product.class);
sum += prod.getPrice();
}
System.out.println("Summe aller Produkte: " + sum);
Die Methode getPrice ist eine treue Operation und bei grossen Datenbeständen ist diese
Variante nicht sehr sinnvoll. Als Alternative sollen die Preise der Produkte im EJB Container
summiert werden. Dazu bauen wir die Methode getAllPriceTotal() und fügen unserem
EJB Local Interfaces hinzu für einen lokalen Zugriff innerhalb des EJB Kontainers:
neue Finder Methode im Home Interface
public double getAllPriceTotal() throws RemoteException;
Alle Methoden des Home Interface müssen durch eine entsprechende MethodenSignature in
der Bean Klasse vertreten sein (analog create(...) Methoden).
Die getHomeAllPriceTotal() Methode könnte folgendermassen aussehen:
getHomeAllPriceTotal()
getHomeAllPriceTotal() Methode in der Bean Implementation
public double ejbHomeGetAllPriceTotal() {
double sum = 0.0;
try {
Collection c = this.ejbSelectAllPrices();
Iterator i = c.iterator();
while (i.hasNext()) {
sum += ((ProductLocal) i.next()).getPrice();
}
} catch (Exception e) { … }
return sum;
}
entsprechende <query> im ejbejb-jar.xml Deployment Descriptor
<query>
<query-method>
<method-name>ejbSelectAllPrices</method-name>
<method-params></method-params>
</query-method>
<result-type-mapping>Local</result-type-mapping>
<ejb-ql> SELECT OBJECT(p) FROM ProductBean As p </ejb-ql>
</query>
Die Local Interfaces sind identisch zu den [Remote]Interfaces mit dem Unterschied, dass die
Local Interfaces keine RemoteExceptions werfen, da sie ja "nur" einen lokalen Zugriff machen.
© 2008 Stephan Metzler
Übung ee6 : EJB QL (CMP 2.0)
Version 3.0
Seite 83 von total 218 Seiten
J2EE – EJB 2.x
15
Übung ee62 : PersonBean mittels BMP Entity Bean
15.1
BMP – Bean Managed Persistence
Entity Beans kapseln Datenbankinhalte durch Objekte, die gemäss der EJB-Spezifikation
instanziierbar und zugreifbar sind. Die Verwaltung der gekapselten Dateninhalte erfolgt durch
ein frei festlegbares Datenbankmanagementsystem, die Verwaltung der Objekte selbst durch
den EJB-Container.
Ziel dieser Technik ist es die Komplexität der Persistenzlogik für den Verwender der
bereitgestellten Bean vollkommen transparent zu gestalten und serverseitig zu realisieren. Die
Familie der Entity Beans selbst zerfällt in zwei Untertypen, welche sich entlang des
Realisierungspunktes der Persistenzlogik separieren:
Bean Managed Persistence
Dem Bean obliegt die Umsetzung der Persistenzlogik
Container Managed Persistence
Die Persistenzlogik wird durch den EJB-Container realisiert
Wie bereits für die Realisierung von Session Beans eingeführt, werden auch zur Publikation der
extern zugänglichen Schnittstellen Home und Remote Interfaces benötigt. Dieser Schnittstellentyp dient auch Entity Beans zur Aufnahme der Verwaltungsoperationen zur Erzeugung
(create) und zur Suche existierender (findByPrimaryKey) EJBs.
Home Interface
Die HomeHome-Schnittstelle zeigt die create-Operation zur Erzeugung einer neuen EJB-Instanz.
Ihre Übergabeparameter dienen zur Konstruktion des neuen Objekts und werden durch die
Bean-Implementierung interpretiert. Ferner enthält die Schnittstelle mit findByPrimaryKey
eine Operation deren Implementierung eine Entity-Bean anhand ihres Primärschlüssels
identifiziert und liefert. Aus diesem Grunde erhält die Methode den zu suchenden Wert vom Typ
des Primärschlüssels als Parameter.
Hinsichtlich der verwendeten Typen zeigt sich bereits hier, dass eine Abbildung der Java Typen
auf die des eingesetzten Persistenzsystems stattfinden muss. Bei BMP wo die EJB selbst die
Persistenz verwalten muss ist diese Abbildung manuell durch den Programmierer
bereitzustellen.
Remote Interface
Die RemoteRemote-Schnittstelle gibt die für Nutzer der Bean zugänglichen Geschäftsfunktionen
wieder. Daher enthält dieser Schnittstellentyp lediglich Operationen zum Zugriff auf die
verwalteten Daten, nicht jedoch zur technischen Verwaltung und Interaktion mit dem BeanContainer.
© 2008 Stephan Metzler
Übung ee62 : PersonBean mittels BMP Entity Bean
Version 3.0
Seite 84 von total 218 Seiten
J2EE – EJB 2.x
Per Konvention muss diese Schnittstelle als Spezialisierung der Schnittstelle EJBObject
definiert sein. Diese Standardschnittstelle definiert allgemeine Interaktionsformen, wie:
-
Löschen (remove)
Vergleich (isIdentical)
Ermittlung des Primärschlüsselwertes (getPrimaryKey)
Vergleich zweier serverseitiger EJB-Objekte (isIdentical)
Wert des Primärschlüssels (getPrimaryKey)
Zur Löschung von datenbankresistenten Objekten wird remove eingesetzt. Der Aufruf dieser
Methode entfernt ausschliesslich die durch die EJB repräsentierten Datenbanktupel, die
programmiersprachliche Objektrepräsentation bleibt jedoch über die gesamte Laufzeit (sofern
nicht durch Gültigkeitsbereiche oder explizite NULL-Setzung explizit anders gehandhabt) intakt.
15.2
BMP Person Bean
Erstellen Sie eine Entity Bean zur Personenverwaltung mit Bean Managed Persistence. Jedes
Personen-Objekt enthält Daten zu Name, Geburtsdatum und Wohnort. Der Name dient als
eindeutige Identifikation und daher datenbankseitig als Primärschlüssel. Die notwendige
Datenbanktabelle wird durch folgenden SQL-Ausdruck erzeugt:
CREATE TABLE PERSON(
Name VARCHAR(20) PRIMARY KEY,
Birthdate DATE,
City VARCHAR(30));
Ansatz:
create und findByPrimary Methoden im BMPPersonHome Interface:
public interface BMPPersonHome extends EJBHome {
public Person create(int year, int month, int day, String name,
String city)
throws CreateException, RemoteException;
public Person findByPrimaryKey(String name)
throws FinderException, RemoteException;
}
einige Business Methoden:
public interface Person extends EJBObject {
public int getAge() throws RemoteException;
public String getCity() throws RemoteException;
public void setCity(String city) throws RemoteException;
Die mit getAge in der Remote-Schnittstelle veröffentlichte Operation soll keinen direkt
abgespeicherten Wert liefern, sondern diesen dynamisch zur Ausführungszeit anhand der
verfügbaren Daten berechnen.
© 2008 Stephan Metzler
Übung ee62 : PersonBean mittels BMP Entity Bean
Version 3.0
Seite 85 von total 218 Seiten
J2EE – EJB 2.x
spezifische Business Methode
public int getAge() {
return (new GregorianCalendar().get(Calendar.YEAR)
- birthdate.get(Calendar.YEAR));
Innerhalb der datenbankzugreifenden Methoden muss durch den Anwender die Abbildung der
Java-Datentypen auf die des verwendeten Datenbankmanagementsystems erfolgen. Die mit
dem Präfix ejb versehenen Methoden zeigen dies für die lesenden und schreibenden DBZugriffe. So kann die im Beispiel für name und city verwendete Java-Repräsentation
String vergleichsweise leicht in den SQL-Typ VARCHAR abgebildet werden sofern alle
Methoden sicherstellen, dass nur konforme Werte eingefügt werden.
Exemplarisch soll das mit den Methoden ejbCreate und setCity gezeigt werden.
setStreet
public void setCity(String city) throws RemoteException {
if (city.length() <= 30) {
this.city = city;
} else {
throw new RemoteException("cannot update city");
}
}
Für programmiersprachliche Typen, die nicht direkt in DB-Typen abbildbar sind, muss im Falle
der Bean Managed Persistence der Bean-Entwickler selbst Sorge für die adäquate Abbildung
tragen. Der Java-Datumstyp GregorianCalendar soll manuell in die durch das DBMS
erwartete ISO 8601-konforme Darstellung überführt werden.
Datumstyp Umrechnung
public String ejbCreate(int y,int m,int d,String name,String city)
throws CreateException {
this.name = name;
this.birthdate = new GregorianCalendar(y, m, d);
this.street = street;
Statement stmt = getStatement(); // open DBMS Connection and return stmt
String s = new String("INSERT INTO PERSON VALUES ('"
+ name + "','"
+ birthdate.get(Calendar.YEAR) + "-"
+ birthdate.get(Calendar.MONTH) + "-"
+ birthdate.get(Calendar.DATE) + "'," + "'"
+ street + "'" + ");");
try {
stmt.executeUpdate(s);
} catch (SQLException e) {
e.printStackTrace();
}
} else {
throw new CreateException("Invalid values supplied");
}
return name;
}
© 2008 Stephan Metzler
Übung ee62 : PersonBean mittels BMP Entity Bean
Version 3.0
Seite 86 von total 218 Seiten
J2EE – EJB 2.x
Analog zur ejbCreate Methode ist der Bean Provider verantwortlich für die Existenz der
ejbRemove, ejbStore, ejbFindByPrimaryKey Methoden. Zusätzlich zu allen notwendigen Bean
Life-Cycle Methoden können sämtliche Business Methoden mitverwaltet werden.
Business Methoden
public int ejbHomeGetAge() {
return 0;
}
public String ejbHomeGetCity() {
return new String();
}
Ansatz für den Client Code
Person p1 = home.create(1981, 1, 1, "Leonie", "cityA");
Person p2 = home.create(1972, 2, 2, "Lukas", "cityB");
System.out.println("Leonies Alter: " + p1.getAge());
System.out.println("Leonies PK
: "+p1.getPrimaryKey());
p1.remove();
Person p3 = home.findByPrimaryKey("Lukas");
System.out.println("Lukas wohnt in : "
+ p3.getCity());
p3.setCity("cityC");
System.out.println("Lukas neuer Wohnort: " + p3.getCity());
System.out.println("Lukas alter Wohnort: " + p2.getCity());
System.out.println("sind p2 und p3 das gleiche Java Objekt ? " + (p2==p3));
System.out.println("Sind p2 und p3 das gleiche EJBObject ? " +
p2.isIdentical(p3));
Überlegen Sie sich wie die Ausgabe aussieht bevor Sie Ihre Applikation testen !
Output:
Leonies Alter: ___
Leonies PK: ______
Lukas wohnt in: ________
Lukas neuer Wohnort: ________
Lukas alter Wohnort: ________
sind p2 und p3 das gleiche Java Objekt ? ________
Sind p2 und p3 das gleiche EJBObject ? ________
© 2008 Stephan Metzler
Übung ee62 : PersonBean mittels BMP Entity Bean
Version 3.0
Seite 87 von total 218 Seiten
J2EE – EJB 2.x
ejbejb-jar.xml (Ausschnitt)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans
2.0//EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<ejb-jar>
<display-name>Ejb1</display-name>
<enterprise-beans>
<entity>
<display-name>PersonBean</display-name>
<ejb-name>PersonBean</ejb-name>
<home>beans.PersonHome</home>
<remote>beans.Person</remote>
<ejb-class>beans.PersonBean</ejb-class>
<persistence-type>Bean</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>False</reentrant>
<security-identity>
<description></description>
<use-caller-identity></use-caller-identity>
</security-identity>
</entity>
</enterprise-beans>
…
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>PersonBean</ejb-name>
<method-intf>Home</method-intf>
<method-name>create</method-name>
<method-params>
<method-param>int</method-param>
<method-param>int</method-param>
<method-param>int</method-param>
<method-param>java.lang.String</method-param>
<method-param>java.lang.String</method-param>
</method-params>
</method>
<trans-attribute>Never</trans-attribute>
</container-transaction>
…
</assembly-descriptor>
</ejb-jar>
© 2008 Stephan Metzler
Übung ee62 : PersonBean mittels BMP Entity Bean
Version 3.0
Seite 88 von total 218 Seiten
J2EE – EJB 2.x
16
Servlets
16.1
Vor- und Nachteile dynamischer Web-Inhalte
Es existieren eine Reihe von Lösungen zur Darstellung aktueller (dynamischer) und
nutzerspezifischer Daten im Internet, welche sich im Hinblick auf Erstellungs- und
Wartungsaufwand sowie in der Performance bei der Bereitstellung von Daten unterscheiden:
- Statische, häufig aktualisierte HTML-Seiten sind eine sehr leistungsfähige Lösung. Jedoch
ist der Aktualisierungs- und Wartungsaufwand nicht vertretbar. Eine individuelle
Datenpräsentation für jeden Nutzer ist nicht möglich.
- Eine weit verbreitete Lösung sind Server-basierte Programme wie CGI (Common Gateway
Interface), die dynamische HTML-Seiten generieren. Diese bieten eine gute Performance,
wenn sie nativ (in Maschinencode) implementiert sind, und erlauben eine
Datenbankanbindung. Die Performance ist jedoch gering, wenn sie in Skriptsprachen (z.B.
Perl) realisiert sind. Das Ablaufen von CGI-Programmen in eigenen Prozessen erzeugt einen
hohen Ressourcenverbrauch (Speicher, Ausführungszeit).
- Web-Server-Erweiterungen stellen eine weitere Variante dar, dynamische HTML-Inhalte zu
erzeugen, z.B. durch das ISAPI von Microsoft und das NSAPI von Netscape. Diese
ermöglichen ebenfalls eine Datenbankanbindung, haben aber den entscheidenden Nachteil,
dass sie nicht portabel sind.
- Eine gute Integration mittels Java bietet die Lösung über Applets. Sie ermöglichen eine gute
Wartung, besitzen aber wiederum den Nachteil, dass nicht alle Browser Applets voll
unterstützen bzw. nur eine bestimmte Version.
- Servlets stellen häufig eine bessere Alternative zu den vorgestellten Technologien dar, um
Daten im Internet oder Intranet dynamisch bereitzustellen. Sie bieten die folgenden Vorteile:
- Web-Browser-unabhängig, da die einzige Voraussetzung auf der Client-Seite die
Unterstützung von HTML ist (JavaScript unter anderem kann bei Bedarf auch genutzt
werden)
- Schnelle Bereitstellung auf der Serverseite, da Servlets innerhalb eines Prozesses als
Thread ablaufen und so lange im Server bleiben (persistent), bis sie explizit entfernt
werden
- Sichere Verarbeitung, da sie nur innerhalb eines Prozesses ablaufen
- Plattformunabhängiger Einsatz, da Servlets in Java entwickelt werden
16.2
Grundlagen
Über das Java Servlet API, welches unter anderem durch die J2EE installiert wird, können Sie
Servlets entwickeln, die dynamische Web-Inhalte zur Verfügung stellen. Servlets können ausser
HTML z.B. auch XML, WML, Bild- oder PDF-Dateien bereitstellen. Prinzipiell ist jedes
Datenformat als Rückgabe möglich. Da Servlets in Java implementiert werden, können Sie z.B.
über JDBC Informationen aus Datenbanken verarbeiten.
16.3
Funktionsweise
Als Client wird ein Web-Browser genutzt. Die benötigten Fähigkeiten des Browsers hängen von
der vom Servlet erzeugten Ausgabe ab. So kann ein Servlet reinen HTML-Code nach dem
HTML-Standard 2.0 erzeugen, aber auch HTML-Code, der zusätzlich Java Script, CSS Style
Sheets und dynamisches HTML enthält.
Über den Web-Browser gibt der Client einen URL an, der ein Servlet auf einem Web-Server
startet. Die Anfrage wird zuerst vom Web-Server entgegengenommen. Dieser erkennt, dass es
© 2008 Stephan Metzler
Servlets
Version 3.0
Seite 89 von total 218 Seiten
J2EE – EJB 2.x
sich um ein Servlet handelt und leitet die Anfrage des Clients an dieses weiter. Das Servlet
wertet übergebene Parameter aus und erzeugt eine Ausgabe, z.B. eine HTML-Seite, die
Informationen zu einem bestimmten Software-Produkt enthält. Dieses wird an den Web-Server
übergeben, der diese wiederum an den Client weiterleitet. Ändern sich nach dem Aufruf des
Servlets Produktdaten, werden die aktualisierten Daten bei der nächsten Anforderung
verwendet.
Die HTML-Seite wird damit dynamisch erstellt (bei Bedarf) und enthält immer die zu diesem
Zeitpunkt aktuellsten Informationen.
http "pull"
Web-Browser
Web-Server
HTML
Servlet-Engine
Applications
Der Teil des Web-Servers, der für die Abarbeitung der Servlets zuständig ist, wird häufig auch
als Servlet-Engine oder Servlet-Container bezeichnet. Durch die Integration eines Servlets in
diesen Container wird es automatisch über die Servlet-Engine dem Web-Server zur Verfügung
gestellt und kann von Clients genutzt werden.
16.4
Lebenszyklus
Web-Server
Start
Process
Requests
Container Shutdown
started destroy Methode
Destroy
Beim ersten Servlet Aufruf erstellt der Container
das Servlet und ruft die init Methode auf
Container ruft die service Methode auf,
übergibt die Request Parameter und
erneuert (leert) das Response Objekt.
Für jeden neuen Auftrag wird ein neuer Thread erzeugt, der das betreffende Servlet ausführt.
Die Threads laufen alle in der gleichen JVM innerhalb des gleichen Prozesses. Dadurch können
sie relativ einfach miteinander kommunizieren, und die Verarbeitung von Client-Anfragen ist
durch leichtgewichtige Prozesse (Threads) nicht sehr ressourcenaufwendig.
Auftrag A
Servlet
basierter
Webserver
JVM
Servlet A
Auftrag B
Servlet B
Auftrag C
Servlet C
Prozess
© 2008 Stephan Metzler
Servlets
Version 3.0
Seite 90 von total 218 Seiten
J2EE – EJB 2.x
16.5
Übung ee7 : Servlet – ein Beispiel
Erstellen eines Trivial-Servlets, das einen HTTP Response (Response von Tomcat Web-Server)
erzeugt. Erstellen Sie dazu ein TOMCAT Java Project. Das entsprechende Plugin muss dazu
installiert sein. Die Installationsanleitung ist im Anhang zu finden.
Wenn durch das Plugin ein TOMCAT Projekt erzeugt wird
ist die Verzeichnisstruktur vorgegeben und ein
entsprechendes "src" Verzeichnis und ein Verzeichnis für
den build-Path "WEB-INF/classes" bereitgestellt.
Erstellen Sie in einer Packagestruktur \mypackage
die Servlet Java Klasse HelloServlet.java und
leiten Sie diese von der Superklasse HttpServlet ab:
@inRequest Parameters send vom Client (Browser)
@inRespons HTML sent to Client (Browser)
Erzeugen Sie eine neue Klasse und definieren Sie die service Methode:
public class HelloServlet extends HttpServlet {
public void service(
HttpServletRequest inRequest,
HttpServletResponse inResponse)
throws IOException, ServletException {
...
}
}
Instanzieren Sie innerhalb der Service Methode ein ResponseWrite Object:
Writer aResponseWriter = inResponse.getWriter();
Spezifizieren Sie den Inhalt als HTML Code:
inResponse.setContentType("text/html");
Erzeugen Sie die gewünschten HTML Seiten folgendermassen:
aResponseWriter.write(<HTML CODE>);
© 2008 Stephan Metzler
Servlets
Version 3.0
Seite 91 von total 218 Seiten
J2EE – EJB 2.x
Ersetzen Sie dabei den <HTML CODE> durch die gewünschten HTML Statements und
definieren Sie diese als String., z.B:
aResponseWriter.write("<html><head>\n");
aResponseWriter.write("<title>1st Servlet</title>\n");
aResponseWriter.write("<body>\n");
aResponseWriter.write("<h1>My first SampleServlet is working
great!</h1>\n");
aResponseWriter.write("</body></html>\n");
Erzwingen Sie den ResponseWriter zur Ausgabe:
aResponseWriter.flush();
web.xml Deployment Descriptor (muss im WEB-INF Verzeichnis sein)
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Servlet Test Application</display-name>
<description>Schreibt eine Begrüssung</description>
<servlet>
<servlet-name>Hello</servlet-name>
<servlet-class>mypackage.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Hello</servlet-name>
<url-pattern>/start</url-pattern>
</servlet-mapping>
</web-app>
server.xml im Verzeichnis [TOMCAT_ROOT]\
[TOMCAT_ROOT]\conf
(wird durch das TOMCAT Plugin automatisch bei Erstellen des Eclipse Projektes erstellt)
<Context path="/ee7"
reloadable="true"
docBase="C:\sbb\workspace\ee71"
workDir="C:\sbb\workspace\ee71\work" />
Ausgabe im Browser Fenster
© 2008 Stephan Metzler
Servlets
Version 3.0
Seite 92 von total 218 Seiten
J2EE – EJB 2.x
16.6
Übung ee71 : Web Browser als EJB Client
Erstellen Sie ein Servlet das alle Produkte der Übung ee6 in einem Browser darstellt. Die
Klassen des EJB bleiben unverändert. Einzig die Schnittstelle zwischen Servlet Engine
(TOMCAT) und EJB-Kontainer (JBoss) muss erstellt werden. Dazu ist es sinnvoll eine Java
Klasse als Bean einzusetzen. Die public Methode getProducts() gibt alle Produkte der
EJB als Collection retour:
Ansatz
public Collection getProducts() throws Exception {
Collection products = null;
try {
Context ctx = new InitialContext();
home = (ProductHome)
PortableRemoteObject.narrow(ctx.lookup("Product"),ProductHome.class);
products = home.findAllProducts();
} catch (Exception e) {
}
return products;
}
Die EJB Bean Klassen sind vorteilhaft im neuen
Projekt untergebracht. Die entsprechende
Verzeichnisstruktur zeigt alle Source Dateien. Die
Client Applikation ProduktClient.java
ermöglicht einen direkten Zugriff auf den EJB
Kontainer (siehe ee6) während das Bean
ProductClientBean.java als Schnittstelle
zwischen Servlet und EJB fungiert.
Um der TOMCAT VM den Zugriff auf den
(REMOTE) EJB-Kontainer von JBoss zu
ermöglichen ist das Archiv
jbossall-client.jar vom Verzeichnis
[JBoss_Home]/client ins /lib Verzeichnis
unterhalb des WEB-INF zu stellen.
Browser Ausgabe
© 2008 Stephan Metzler
Servlets
Version 3.0
Seite 93 von total 218 Seiten
J2EE – EJB 2.x
17
JSP JavaServer Pages
Pages
Sun Microsystems hat die JavaServer Pages (JSP) für die Realisierung der
Präsentationsschicht einer Webanwendung entwickelt. Sie integrieren Businesskomponenten
und existierende Systeme elegant ins Web. Die JSP-Technik basiert auf dem Java-Servlet-API
und dient im Wesentlichen zur einfachen Erzeugung von HTML- und XML-Ausgaben eines
Webservers. Wie PHP, ASP oder Perl können Autoren direkt HTML oder XML mit einer
Skriptsprache mischen. Natürlich hat sich Sun hier für Java entschieden, um so die
Möglichkeiten der vorhandenen robusten und zahlreichen Java-APIs einzubringen.
Wie auch Servlets ermöglichen JSP die Erstellung dynamischer Webinhalte mit dem
Unterschied das JSP aus statischem HTML und dynamischen JAVA Elementen besteht.
17.1
Grundidee
- Ähnliche Vorgehensweise wie bei Server Side Includes, aber unter Benutzung von JavaAnweisungen.
- Funktionalität und Syntax erinnern an Active Server Pages (ASP).
- Blocks von Servlet Code (Scriptlet) werden in statische HTML- Seiten eingefügt.
17.2
Funktionsweise
http
Web-
HTML
(http)
Web-Server
JSP-Engine
(HTML)
1. Aufruf
compiler *.class
HTML
JSP-Seite
Servlets
JSP
Java-Datei
(Servlet)
Eine JSP-Seite ist ein Textdokument und besteht aus:
-
HTML- Code
JSP-Code
JSP-Tags
Java Anweisungen
Beim ersten Client Request auf eine JSP Seite analysiert die JSP-Engine des Web-Servers
(z.B. Tomcat) das HTML Dokument und erstellt dynamisch ein Servlet. Alle weiteren Client
Aufrufe werden dann vom automatisch erstellten Servlet behandelt. Die Umwandlung der JSPSeite in ein Servlet ist für den Entwickler und auch für den Anwender völlig transparent.
17.3
Organisation
Web Applications sind zusammengehörende Webserver-Programmpakete mit einer definierten
Hierarchie-Struktur. Sie werden im Webserververzeichnis \webapps als war Archive
gespeichert.
© 2008 Stephan Metzler
JSP JavaServer Pages
Version 3.0
Seite 94 von total 218 Seiten
J2EE – EJB 2.x
17.4
Aufbau von JavaServer Pages
Ressourcen
- HTML-Datei
- JSP-Datei
Datei
- ASCII- Datei
- JSP-Datei
17.5
- Ausdrücke
- Skiptlets
- Deklarationen
- Direktiven
Java Klasse
Tag-Libraries
- JSP-Tags
JSP-Seite
JSP-Seite
- Ausdrücke
- Skriptlets
- Deklarationen
- Direktiven
Java Bean
Eigenschaften
- setter
- getter
Kommentare
Kommentare, die nicht im generierten File auftauchen sollen, haben folgende Form:
<%-- ... JSP- Kommentar... --%>
Beispiel:
17.6
<%-- Das ist ein JSP Kommentar --%>
Ausdrücke / Expressions
Eine Expression beginnt mit <%= und endet mit %>.
Jeder Java- Ausdruck in der Expression wird ausgewertet, in einen String konvertiert und der
Ergebnistext direkt in die Seite eingefügt.
Eine Expression <%= count %> würde übersetzt in out.println(count); und würde
den Inhalt der Variablen count in Textform in die Ergebnisseite einfügen.
Beispiel
<%= "Hallo" %><br>
<%= "zwei + drei : " 2 + 3 %><br>
<%= new Date() %><br>
Ausgabe:
Hallo
zwei + drei : 5
Thu Jan 01 00:08:30 GMT+01:00 2000
© 2008 Stephan Metzler
JSP JavaServer Pages
Version 3.0
Seite 95 von total 218 Seiten
J2EE – EJB 2.x
17.7
Direktiven
Direktiven sind Informationen für die JSP Engine, um die Auswertung der JSP-Seite zu
steuern. Eine Direktive hat die Form einer Variablenzuweisung: <%@ type varname=“value“ %>.
- includeinclude Direktiven, um Inhalte anderer Dateien einzufügen:
<%@ include file=“head.jsp“ %>
- pagepage Direktiven zur Kontrolle bestimmter Aspekte des Servlets:
<%@ page content-type=“text/plain“ %>
- contentcontent- type:
type definiert den Content-Type der erzeugten Seite.
Default: “text/ html”:
<%@ page content-type=“text/plain” %>
- import:
import übergibt eine kommaseparierte Liste der Importe:
<%@ page import=“java.io.*,java.util.*” %>
- extends:
extends spezifiziert die Elternklasse des Servlets:
<%@ page extends=“MyHttpServlet” %>
- implements:
implements übergibt eine kommaseparierte Liste der Interfaces:
<%@ page implements=“Serializable”%>
- method:
method Spezifiziert die Servletmethode, die den generierten Code enthalten und ClientRequests behandeln soll. Default: “service”:
<%@ page method=“doPost”%>
- language:
language Spezifiziert die Back-End Scriptsprache Default: “java”:
<%@ page language=“java”%>
Beispiel:
<%@ page language="java" info="JSP Beispiel" %>
<%@ page import="java.util.*" %>
<%@ include file="mein_eigenes.jsp" %>
17.8
Deklarationen
Deklarationen dienen zur Definition von Methoden und Attributen. Sie werden durch ein Tag
<%! ... Deklaration... %> definiert.
Beispiel:
<%! int i = 0 %>
<%! private int getNextInt() {
i++;
return i;
}
%>
© 2008 Stephan Metzler
JSP JavaServer Pages
Version 3.0
Seite 96 von total 218 Seiten
J2EE – EJB 2.x
17.9
Skriptlets
Definieren Blöcke zur Programmierung in der eingestellten Sprache. Der Code eines Skriptlets
wird erst nach einem Request ausgeführt.
- Scriptlet-Tag wird eingeleitet durch <% und beendet durch %> .
- Ein Scriptlet kann folgende vordefinierte Variablen benutzen:
- request : HttpServletRequest Objekt
- response : HttpServletResponse Objekt
- out : PrintWriter Objekt
- in : BufferedReader
Beispiel:
<%@ page import = "java.util.*" %>
<html>
<head>
<title>Zufallsfarbe></title>
</head>
<% if(new Random.()nextDouble() > 0.5) { %>
<body bgcolor="#FF0000">
<% } else { %>
<body bgcolor="#00FF00">
<% } %>
</body>
</html>
generierter ServletServlet-Code:
out.write("<html>\r\n <head>\r\n ");
out.write("<title>Zufallsfarbe></title>\r\n ");
out.write("</head>\r\n ");
if(new java.util.Random.()nextDouble() > 0.5) {
out.write("<body bgcolor="#FF0000">\r\n ");
} else {
out.write("<body bgcolor="#00FF00">\r\n ");
}
out.write("</body>\r\n </html>\r\n ");
Alternative zum Beispiel:
<%@ String bg;
if (new java.util.Random.()nextDouble() > 0.5)
bg = "<body bgcolor=\"#FF0000\">";
else
bg = "<body bgcolor=\"#00FF00\">"; %>
<%= bg %>
© 2008 Stephan Metzler
JSP JavaServer Pages
Version 3.0
Seite 97 von total 218 Seiten
J2EE – EJB 2.x
17.10
Aktionen
Aktionen sind Anweisungen an die JSP-Engine.
- Aktionen werden verwendet:
- bei der Verwendung von JavaBeans
- beim Erzeugen von Tag Libs (eigener Aktionen)
- zum Einbinden externer Ressourcen
- Integration in JSP-Seite durch XML-Syntax
- Beschreibung durch Attribute
- Beginnt mit <jsp:aktion Tag und wird durch /> Tag abgeschlossen
- Standardaktionen (vordefiniert):
- <jsp:forward page>
- <jsp:plugin>
-
<jsp:getProperty>
<jsp:setProperty>
<jsp:include>
file="...
page="...
- <jsp:useBean>
- Attribute:
- id=
- scope=
- page
- request
- session
- application
zur Weiterleitung an eine andere Ressource
zum Einbinden von Applets
liest den Wert einer Bean-Eigenschaft
setzt den Wert einer Bean-Eigenschaft
zum Einbinden externer Ressourcen
- zur Übersetzungszeit
- bei Anforderung
ermöglicht den Zugriff auf Java-Beans
definiert eindeutiger Name
definiert Gültigkeitsbereich des Objektes:
- innerhalb dieser Seite
- Verzweigungen innerhalb dieser Anforderung
- innerhalb der Session
- innerhalb der Applikation / Web-Anwendung
Beispiel:
<jsp:useBean id="myBean" class="MyBean" scope="page"/>
17.11
Fehlerbehandlung
Es muss unterschieden werden zwischen:
Übersetzungsfehler :
- übermittelt einen HTTP-Fehler 500 (Server Error) an den Client
Ausführungsfehler :
- in der entsprechenden catch Anweisung
- nichtbehandelte Laufzeitfehler leiten den Request an die entsprechende URL
(Attribut: errorPager) weiter
© 2008 Stephan Metzler
JSP JavaServer Pages
Version 3.0
Seite 98 von total 218 Seiten
J2EE – EJB 2.x
17.12
Vordefinierte Variablen
Innerhalb einer JSP-Seite sind einige Variablen vordefiniert, die innerhalb von Scriptlets und
Ausdrücken verwendet werden können:
17.13
application (ServletContext)
speichert Daten, die von mehreren JSP-Seiten
genutzt werden können, z.B. DB Zugriff
config (ServletConfig)
übergibt Initialisierungen der ServletKonfiguration
exception
enthält Exception-Objekt bei einer Exception,
falls: <%@ page errorPage = "true" %>
out(PrintWriter)
gibt die Daten an die HTML-Seite aus
page(this)
stellt die Referenz auf das Servlet während der
Laufzeit der JSP-Seite zur Verfügung
pageContext (PageContext)
ermöglicht den Zugriff auf die Attribute der JSPSeite
request (HttpservletRequest)
enthält Informationen über die aktuellen
Anforderungen an die JSP-Seite
(Parameter, etc.)
response (HttpServletResponse)
bearbeitet den HTTP Response Header / HTTP
Status Code
session (Httpsession)
speichert Informationen über die aktuelle
Session
Übung ee8: JSP Währungsrechner mit JDBC, TagLib und EJB
Diese Übung beschreibt eine vier Layer Web Anwendung mit JSP's und EJB. Nachfolgend das
Deployment Diagramm mit den Komponenten:
CLIENT-TIER
Browser
Browser
WEB-TIER
Tomcat
JSP
HTTP
localhost:8080/ee8
LOGIG-TIER
JBoss
DATA-TIER
mySQL
EJB
RMI
JavaServerPages
Currency.jsp
Currency_CDBC.jsp
Currency_DBTags.jsp
Rates
JDBC
MoneyExchange
Stateless Session
ee
- rates
ID,BASECUR,RATE
Die Anwendung soll die Umrechnung eines Betrages von einer Währung in eine andere
ermöglichen. Die Währungen und die entsprechenden Wechselkurse sind in der mySQL DB
abgelegt. Das Stateless Session Bean wird zur Umrechnung (Business Logik) benutzt. Für die
JDBC Anbindung von einem JSP wird die Custem TagLib dbtag.jar benutzt. Informationen
zu dieser TagLib und die notwendigen jar Archive sind erhältlich von
http://jakarta.apache.org/taglibs/
© 2008 Stephan Metzler
JSP JavaServer Pages
Version 3.0
Seite 99 von total 218 Seiten
J2EE – EJB 2.x
Erstellen Sie eine entsprechende Applikation mit JSPs. Benutzen Sie dazu die
Referenzbeispiele der lokalen TOMCAT Installation.
Ansatz : Browser Window
Ansatz: JSP mit DBTags um mySQL DB abzubinden
<%@ taglib uri="http://jakarta.apache.org/taglibs/dbtags" prefix="sql" %>
<%-- open a database connection --%>
<sql:connection id="conn1">
<sql:url>jdbc:mysql://localhost:3306/ee</sql:url>
<sql:driver>org.gjt.mm.mysql.Driver</sql:driver>
<sql:userId>root</sql:userId>
<sql:password> </sql:password>
</sql:connection>
<%-- get the requestet parameter --%>
<sql:statement id="stmt1" conn="conn1">
<sql:queryselect id from rates order by 2>
<sql:resultSet id="rset1">
<sql:getColumn position="1"/>
</sql:resultSet>
</sql:statement>
<%-- close a database connection --%>
<sql:closeConnection conn="conn1"/>
Ansatz: JSP mit JDBC um mySQL DB anzubinden
<%@ page import="java.sql.*" %>
<%
String con = "jdbc:mysql://localhost:3306/ee";
String user = "root";
String pass = "";
Class.forName("org.gjt.mm.mysql.Driver");
Connection connection = DriverManager.getConnection(con,user,pass);
Statement statement = connection.createStatement();
SQLQuerry = "select basecur,rate from rates where id = " + jdbc_from;
rs = statement.executeQuery(SQLQuerry);
rs.next();
String base = rs.getString(1);
String exch_rate = rs.getFloat(2);
statement.close();
connection.close();
%>
© 2008 Stephan Metzler
JSP JavaServer Pages
Version 3.0
Seite 100 von total 218 Seiten
J2EE – EJB 2.x
18
J2EE Security
18.1
EJB-Security
Der grundlegende Gedanke von J2EE-Anwendungen ist es, sowenig wie möglich
programmatische Sicherheit in die Beans zu codieren, sondern vielmehr deklarative
Sicherheitseinstellungen über den Server tätigen zu können.
Während ältere J2EE-Spezifikationen die Sicherheit nur am Rande behandelten, wurde sie seit
der Version 2.0 zunehmends genauer reglementiert. Zentrale Themen und Neuerungen sind
hierbei die Authentisierung von Clients und sichere Kommunikationsformen zwischen Client und
Server. Weiterhin ist die Kommunikation zwischen Containern zunehmends gesichert worden.
Die Umsetzung eines rollenbasierten Zugriffsschutzes wird durch die "Security Identity
Propagation" innerhalb des Systems ermöglicht, also dem Weiterreichen der Identität eines
EJB-aufrufenden Client. Seit der EJB-Spezifikation Version 2.1 ist die Applikation-ClientAuthentisierung über den Java Authentication and Authorisation Service (JAAS) möglich.
Ein Client wird bei seiner Anmeldung am System zunächst als ein unidentifiziertes Subject
angesehen. Nach erfolgreicher Authentisierung werden diesem Client-Subject (u.A. mehrere)
sogenannte Principals und Credentials zugeordnet. Principals werden über das
java.security.Principal-Interface implementiert und assoziieren das ClientSubject mit einem Benutzernamen oder –konto. Beispiel für ein Principal wäre z.B "Herr Meier,
... " oder "meier@inf...". Credentials sind Objekte, die sicherheitsrelevante Daten wie etwa ein
Kerberos-Ticket oder Zertifikate des Clients speichern. Einfachste Möglichkeit wäre das
Loginpasswort o.Ä. darin abzulegen. Folgendes Diagramm veranschaulicht die Zuordnung von
Principals und Credentials an ein Subject:
0..*
interface
Principal
SamplePrincipal
0..*
Object
SamplePrincipalCredential
Subject
public Credentials
0..*
private Credentials
SamplePrivateCredential
Principals werden später wiederum bestimmten
bestimmten Rollen zugeordnet,
zugeordnet die Teil der umzusetzenden
Sicherheitspolitik des Systems sind. Man spricht dann von "principal-to-role-mapping". Es ist
nach Spezifikation für J2EE-Server erforderlich, ein Tool zur Verfügung zu stellen, das dieses
Mapping übernimmt (z.B Abgleich mit einer Datenbank). Innerhalb des Systems werden die
Principals und mit ihnen die aufgesetzten Rollen von Aufruf zu Aufruf der EJB weiterpropagiert
und sind auch über den EJBContext innerhalb des EJB-Codes jederzeit abfragbar.
© 2008 Stephan Metzler
J2EE Security
Version 3.0
Seite 101 von total 218 Seiten
J2EE – EJB 2.x
Eine unverschlüsselte Datenübertragung kommt besonders für Geschäftsanwendungen
heutzutage selten noch in Frage, da ansonsten eine vertrauliche Übermittlung von Kunden- und
Logindaten nicht gewährleistet werden kann. Es sind also sichere Übertragungsverfahren
erforderlich, wobei SSL (Secure Sockets Layer) die zentrale Rolle bei J2EE–konformen
Anwendungen spielt. SSL bietet die Möglichkeit zur Datenverschlüsselung und Überprüfung der
Datenintegrität (über eine TCP/IP-Verbindung).
Es stehen Mechanismen bereit, die es erlauben, authentisierten Clients Rollen zuzuordnen und
diese durch das gesamte System zu propagieren. Der Container kann "automatisch" prüfen, ob
eine Rolle oder Identität überhaupt Zugriff auf ein bestimmtes Bean hat, dazu ist nicht
unbedingt eine Abfrage innerhalb des Bean-Codes notwendig. Bei der J2EE-Architektur ist es
möglich, für jedes EJB anzugeben, welche Rolle zum Aufruf welcher Methode berechtigt ist.
Ein aufrufendes Subject kann einerseits direkt eine Client-Anwendung sein, aber auch ein
anderes EJB. Vom Client aufgerufene EJB propagieren dessen Identität und zugeordnete
Rollen zu anderen EJB, die im Zuge der Programmausführung aufgerufen werden.
Als Option steht allerdings noch die
Möglichkeit offen, EJB immer dieselbe
Rolle propagieren zu lassen, unabhängig
vom aufrufenden Subject. Ein Bean kann
z.B immer die Rolle "Guest" propagieren,
um etwa den niedrigsten Sicherheitslevel
einzunehmen, egal ob der Aufrufende nun
in die Rolle "Customer" oder "Admin" fällt.
…
<enterprise-beans>
…
<session>
<ejb-name>EJBSecurity</ejb-name>
…
<security-identity>
<runAs-specified-identity>
<role-name>guest</role-name>
</runAs-specified-identity>
</security-identity>
…
</session>
…
</enterprise-beans>
© 2008 Stephan Metzler
J2EE Security
Version 3.0
Seite 102 von total 218 Seiten
J2EE – EJB 2.x
18.1.1
Security Roles
Security Rollen werden in verschiedenen Entwicklungsbereichen definiert. Die Bearbeitungsschritte unterscheiden sich u.A. in der Art der Deklarierung im Deploymentdescriptor.
Bean Provider (EJB(EJB-Entwickler)
Der Bean Provider, also derjenige der die EJB programmiert, soll "so wenig wie möglich" mit
Sicherheitsfragen belangt werden. Programmierer sollen sich grösstenteils auf die Erstellung
von wiederverwendbaren Komponenten und die Umsetzung von Geschäftsabläufen
konzentrieren. Die jeweilige Sicherheitspolitik soll nicht hart im Code eingebettet sein, sondern
später über den Container kontrollierbar und konfigurierbar sein.
Ist es nicht möglich, auf die "programmatische Sicherheit" zu verzichten (z.B. Aktionen, die von
der Tageszeit oder von einem Konto-Betrag abhängig sind) muss dieses innerhalb des Beans
implementiert werden. Die zur Überprüfung dieser Bedingungen nötigen Werte werden
schliesslich erst zur Laufzeit bestimmt und verändert. Dem Programmierer ist es innerhalb des
EJB-Codes möglich, die Rollen des aufrufenden Subjects zu ermitteln. Er kann dann im Code
den Zugriff direkt und dynamisch kontrollieren. Über das vom Container bereitgestellte
EJBContext-Objekt javax.ejb.EJBContext für EJB bzw. HttpServletRequest-Objekt für
beispielsweise JSP, können die folgenden Methoden aufgerufen werden:
ejbContext.getCallerPrincipal()
ejbContext.isCallerInRole("admin"); httpServletRequest.isUserInRole("admin");
httpServletRequest. getUserPrincipal();
Die Methode getCallerPrincipal() bzw. getUserPrincipal() hat als
Rückgabewert ein Principal-Object, das die Identität des Aufrufenden Subjects beinhaltet. Mit
Hilfe von isCallerInRole(String role) bzw. isUserInRole(String role) ist
die direkte boolsche Abfrage der Rollenzugehörigkeit möglich. Da vom Standpunkt des
Programmierers aus noch keine systemweiten Rollen eingeführt wurden, kann der
Programmierer über den Deploymentdescriptor eigene Rollen einführen. Diese Rollen sollten
grösstenteils allgemein und logisch auf eine Geschäftsstruktur angewendet werden können. So
wird es höchstwahrscheinlich in einem Bank-EJB, das Kontendaten abruft, eine Rolle "Kunde"
bzw. "Angestellter" oder "Admin" geben. Es müssen dazu im Deploymentdescriptor
Rollenreferenzen eingeführt werden. Der Entwickler erstellt damit vorerst logische Rollen, die
später vom Assembler den tatsächlichen Rollen im System zugeordnet werden.
…
<security-role-ref>
<description>
This role encompasses the administrators of the company.
</description>
<role-name>admin</role-name>
</security-role-ref>
…
Später ist es möglich, die letztendlichen Rollen des Systems im Deploymentdescriptor auf diese
logischen Rollen des Programmierers abzubilden.
© 2008 Stephan Metzler
J2EE Security
Version 3.0
Seite 103 von total 218 Seiten
J2EE – EJB 2.x
Application Assembler
Der Applicationassembler hat die Aufgabe das System aus den EJB-Komponenten
zusammenzustellen und verkaufsfertig zu konfigurieren. In Bezug auf Sicherheit deklariert er auf
den späteren Einsatzzweck zugeschnittene Rollen. Diese setzen auf den Rollen des BeanProviders auf und müssen auch diesen zugeordnet werden.
Ohne Zuordnung der Rollenreferenzen auf Rollen des Systems sind diese Rollenreferenzen
ohne Bedeutung. Diese erzwungene Zuordnung von logischen Rollen auf die tatsächlich
verwendeten Rollen verhindert, dass der Programmierer nur ihm bekannte Rollen einführen
kann. Eine geheim deklarierte Rolle wie etwa "Programmierer" müsste also auch vom
Assembler/Deployer deklariert und abgesegnet werden, um im System wirken zu können.
Somit wird diese Art von programmatischen Hintertürchen vermieden. Weiterhin muss der
Assembler festlegen, welche spezifischen Methoden der einzelnen EJB von welcher Rolle
aufgerufen werden dürfen. Dies geschieht mittels method permissions.
Diese Permissions werden für jedes Bean einzeln vergeben. Alle diese Schritte manifestiert der
Assembler wieder im Deploymentdescriptor.
…
<assembly-descriptor>
<security-role>
<description>
This role includes the guests who are allowed limited access.
</description>
<role-name>guest</role-name>
</security-role>
<security-role>
<description>
This role includes the customers.
</description>
<role-name>customer</role-name>
</security-role>
<security-role>
<description>
This role should be assigned to the personnel
(authorized to perform administrative functions).
</description>
<role-name>staff</role-name>
</security-role>
…
</assembly-descriptor>
…
Der nächste Auszug zeigt die Einführung der method-permissions für ein einzelnes Bean. Zu
den jeweiligen Rollen (guest, customer, staff) werden die für sie erlaubten Methoden deklariert.
Innerhalb des <method-name>-Elements können die folgenden drei Werte stehen:
- der Name der erlaubten Methode
- der Name und eventuelle Parameter, um sie eindeutig zu identifizieren
- ein *, um alle Methoden für eine Rolle freizugeben
Das <method-intf> Element definiert das entspechende Interface :
- 'Home', 'Remote', 'LocalHome' oder 'Local'
© 2008 Stephan Metzler
J2EE Security
Version 3.0
Seite 104 von total 218 Seiten
J2EE – EJB 2.x
<method-permission>
<role-name>guest</role-name>
<method>
<ejb-name>EJBSecurity</ejb-name>
<method-intf>Home</method-intf>
<method-name>*</method-name>
</method>
<method>
<ejb-name>EJBSecurity</ejb-name>
<method-intf>Remote</method-intf>
<method-name>getPrincipalInfo</method-name>
</method>
</method-permission>
…
<method-permission>
<role-name>staff</role-name>
<method>
<ejb-name>EJBSecurity</ejb-name>
<method-name>*</method-name>
</method>
<method-permission>
Deployer
Um eigene Rollen zum System hinzufügen zu können, ist der Deployer auf eine gute
Spezifikation des Assemblers angewiesen und muss über Rollenreferenzen des Programmierers
Bescheid wissen. Es ist Aufgabe des Deployers, das auf seine Bedürfnisse angepasste
Sicherheitssystem zu administrieren.
Es muss in diesem Stadium, d.h. während der Ausführung durch den Deployer, eine
letztendliche Zuordnung von Benutzern bzw. Benutzergruppen auf die Rollen festgelegt werden.
Zu diesem Zweck erfordert es die J2EE-Spezifikation, dass mit einer Serverimplementierung
bestimmte Tools zur Verwaltung mitgeliefert werden. So u.A. für die allgemeine Administration
des Rollensystems und der Abbildung von Benutzern und Benutzergruppen auf diese Rollen.
Dieses Principal-to-Role-Mapping ist jeweils nur für eine einzelne Applikation innerhalb des
Containers gültig, d.h. nur für ein Applikations-jar-File. Für jedes einzelne jar-File müssen
Principals also separat auf Rollen abgebildet werden. Das liegt daran, dass ein
Deploymentdescriptor nicht applikationsübergreifend lesbar ist, sondern nur für die Applikation
gilt, in der er enthalten ist. In ihm werden schliesslich die Rollen deklariert, die für jede
Anwendung anders sein können und individuell "gemappt" werden müssen.
PrincipalPrincipal-toto-RoleRole-Mapping
<security-role-ref>
<description>
This role encompasses the administrators of the company.
</description>
<role-name>admin</role-name>
<role-link>staff</role-link>
</security-role-ref>
© 2008 Stephan Metzler
J2EE Security
Version 3.0
Seite 105 von total 218 Seiten
J2EE – EJB 2.x
18.2
Client-Tier Sicherheit
18.2.1
ApplicationClients
Seit der Version 2.0 ist es für J2EE-kompatible Server verpflichtend, eine Unterstützung für
den Java Authentication and Authorisation Service (JAAS) zu bieten. Damit ist es möglich,
Application-Clients zu authentifizieren. JAAS implementiert eine Version des Pluggable
Authentication Module (PAM) Frameworks und erlaubt den Einsatz von eigens entwickelten
Loginmodulen. Diese Loginmodule werden auf Seiten des Clients eingesetzt. So ist es
verschiedenen Applicationclients möglich, auch verschiedene Loginvarianten zu unterstützen.
Beispielsweise wäre neben einem simplen Modul für Name/Passwort-Dialoge auch ein Modul
denkbar, das mit Hilfe von externer Hardware und Smartcards arbeitet, oder Zertifikate
weiterreicht. Die Loginmodule selbst steuern den Loginvorgang und sammeln
Authentisierungsdaten über sogenannte Callbackhandler. Darüber werden auch
Satusinformationen vom Server an den Client zurückgereicht. Die jeweilige Applikation auf
Client-Seite muss dazu dem Server u.A. mindestens eine Klasse anbieten, die das Interface
javax.security.auth.callback.CallbackHandler implementiert. Durch die
Benutzung über solche Interfaces bleibt die Implementierung von Loginmodulen austauschbar.
Ist der Einsatz von JAAS nicht erwünscht, kann der Container den Vorgang der Authentisierung
über voreingestellte Mechanismen komplett selbst übernehmen. Die Vorgehensweise dabei ist
allerdings abhhängig von der Implementierung. Dazu ist die Clientsoftware entsprechend
einzurichten.
MyCallbackHandler, die javax.security.auth.callback.CallbackHandler implementiert.
MyCallbackHandler handler = new MyCallbackHandler(name, password);
LoginContext lc = new LoginContext("default", handler);
lc.login();
18.2.2
WebClients
Die Spezifikation erlaubt prinzipiell drei Arten von Webclient-Authentisierung, die über den
Server einstellbar sein müssen:
- http bzw. http über SSL (https) – Es werden http-Formulare mit einfacher Login/PasswortAbfrage benutzt.
- formular – Agiert auch über http/https. Diese Einstellung erlaubt jedoch eigens definierte
und umfangreichere Abfragen. Es können Mechanismen wie Cookies bzw. Session-Ids
benutzt werden.
- client-certificate – Erlaubt die Authentifizierung von Webclients über Zertifikate. Für diese
Option muss zur gegenseitigen Authentisierung (mutual authentication) natürlich auch auf
Client-Seite ein X.509-Certificate installiert sein.
Der im Mid-Tier genauer beschriebene rollenbasierte Zugriffsschutz wird auch bei
Webapplikationen vom Container übernommen. Es sind auch bei Webanwendungen
programmatische statt vom Container übernommene Sicherheitsabfragen möglich: Über das
HttpServletRequest Interface kann mit der Methode isUserInRole festgestellt
werden, ob der Benutzer in einer bestimmten Rolle ist. Um die Identität (in Form eines
java.security.Principal) zu erhalten, kann die Methode getUserPrincipal
verwendet werden.
© 2008 Stephan Metzler
J2EE Security
Version 3.0
Seite 106 von total 218 Seiten
J2EE – EJB 2.x
18.3
JNDI Sicherheit
Möchte eine Client-Anwendung z.B. eine Methode eines (Session- oder Entity-) EJB aufrufen,
erhält es nach Anfrage an das Java Naming and Directory Interface (JNDI) einen Verweis auf
ein EJB-HomeInterface. Über dieses Interface generiert er einen Verweis auf das RemoteInterface des EJB, über das dann die (Geschäfts-) Methoden des EJB aufgerufen werden
können. Für den lokalen Zugriff müssen Beans auch einen Satz Local Interfaces bereitstellen.
Darüber können dann andere Beans innerhalb des Containers oder Webanwendungen auf denselben Server zugreifen. Es müsste im J2EE-Server somit ein Mechanismus implementiert sein,
um Mutual Authentication mit dem JNDI-Dienst zu ermöglichen, was aber bislang selten der
Fall ist. Gelänge es, einem Client oder dem Container einen falschen JNDI-Namespace
zuzuspielen, könnte man ihnen darüber auch falsche Home-Interfaces und folglich falsche und
böswillige Objektreferenzen zukommen lassen. Authentisierungsdaten könnten so z.B durch
spoofing eines Loginvorgangs abgefragt werden. Andererseits muss auch der JNDI-Dienst
gesichert werden, da sonst auch unberechtigte Server Einträge in dem Dienst vorgenommen
werden können.
Die J2EE-Spezifikation rät zur Sicherung der Name-Services und zur Verwendung von "CORBA
specified naming services" (Cos-Naming) über eine sichere Verbindung. Die Umsetzung
allerdings ist den Serverentwicklern überlassen, da auch die verschiedenen CORBAImplementierungen nicht immer eine Sicherung ihrer Namensdienste unterstützen.
Im folgenden sei eine Client-Initialisierung des JNDI-Kontextes exemplarisch dargestellt:
…
// Set up the environment properties
Hashtable h = new Hashtable();
h.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.cosnaming.CNCtxFactory");
h.put(Context.PROVIDER_URL, "iiop://localhost:3700");
h.put(Context.SECURITY_PRINCIPAL, "username");
h.put(Context.SECURITY_CREDENTIALS, "password");
// Get an InitialContext
return new InitialContext(h);
…
18.4
Realms
Bei der J2EE-Spezifikation werden Realms als eine Gruppe von Benutzern definiert, die alle mit
dem selben Mechanismus authentisiert werden (ein Realm wäre etwa "certificate" und "ldap",
wie beim Sun ONE Application Server). Realms werden auch manchmal als "security policy
domains" bezeichnet. Innerhalb dieser Realms gelten dann die Zuordnungen von Rollen an
Benutzer. Bei der Verwendung verschiedener Loginverfahren über JAAS ist es möglich, das
System in Realms für jedes Verfahren einzuteilen. Das ist aber abhängig von der jeweiligen
Implementierung, ebenso wie z.B. Einteilungen in LDAP-Realms und Betriebssystem-Realms.
18.5
Legacy Tier
Es gibt zwei Möglichkeiten, den Container, bzw. die Beans die in ihm laufen, gegenüber
Legacy-Systemen wie etwa Datenbanken oder Gateways zu authentisieren. Die Systeme an
sich erfordern meist die Eingabe eines Namen und Passworts oder benutzen andere
Loginvarianten.
Die beiden Möglichkeiten sind "Container-Managed" und "Bean-Managed".
© 2008 Stephan Metzler
J2EE Security
Version 3.0
Seite 107 von total 218 Seiten
J2EE – EJB 2.x
18.6
Declarative vs Programmatic Security
Zugriffskontrolle
Web Tier
EJB Tier
Web Resources
Bean Methods
deklarative
Sicherheit
programmatische
Sicherheit
Deployment
Descriptor
Programm
Deklaration
web.xml
ejb-jar.xml
Umsetzung
Web Container
EJB Container
Container
Programm
Codierung
Servlet / JSP
EJB Bean
"all or nothing"
Logikbasiert
18.7
J2EE Security Architecture
18.8
Role Mapping
User: "admin"
User: "user"
Principal: admin
User: "guest"
Principal: user
Role: staff
Principal: guest
guest
JBOSS
(Vendorspecific)
Role: customer
J2EE
Role Reference:
staff
© 2008 Stephan Metzler
J2EE Security
Role Reference:
customer
Version 3.0
Seite 108 von total 218 Seiten
J2EE – EJB 2.x
18.9
ejb-jar.xml Security Elemente
Die folgende Graphik zeigt die Security Related Elements des ejb-jar.xml Deployment
Descriptors.
© 2008 Stephan Metzler
J2EE Security
Version 3.0
Seite 109 von total 218 Seiten
J2EE – EJB 2.x
18.10
Übung ee9 : J2EE Security
Erstellen Sie ein ApplicationClient der sich am JBoos Applikationsserver mittels username /
password identifiziert und anschliessend seiner Security Policy entsprechende
Businessmethoden aufruft:
Ansatz: SecurityTestClient
public class SecurityTestClient {
public static void main(String args[]) throws Exception {
/* username,
* admin
* user
* guest
*/
password,
admin
user
guest
role
staff
customer
guest
String name = "admin";
String pass = "admin";
String role = "staff";
char[] password = pass.toCharArray();
System.setProperty("java.security.auth.login.config",
"[JBoss-Home]/client/auth.conf");
//
//
//
//
[Jboss-Home]/client/auth.conf:
SecurityTest {
org.jboss.security.ClientLoginModule required debug=false;
};
try {
AppCallbackHandler handler = new AppCallbackHandler(name, password);
LoginContext lc = new LoginContext("SecurityTest", handler);
lc.login();
Properties env = new Properties();
env.load(new FileInputStream("resources/jndi.properties"));
InitialContext initial = new InitialContext(env);
SecurityTestHome home = (SecurityTestHome)initial.lookup("ejb/Security");
SecurityTest st = home.create();
System.out.println("Principal : " + name +
" wants to getPrincipalInfo : " + st.getPrincipalInfo(role));
System.out.println("Principal : " + name +
" wants to doSomething
: " + st.doSomething(role));
st.remove();
lc.logout();
} catch (Exception e) {
System.err.println("Failed :");
Throwable t = e;
while (t != null) {
System.err.println("Type: " + t.getClass().getName());
System.err.println("Message: " + t.getMessage());
t = t.getCause();
}
}
}
}
© 2008 Stephan Metzler
J2EE Security
Version 3.0
Seite 110 von total 218 Seiten
J2EE – EJB 2.x
18.10.1 deklarative Security
Die Security Policies für die "user" und die "roles" werden in
zwei text-basierten Dateien abgebilded und im root
Verzeichnis in der JAR Datei bereitgestellt:
users.policies
admin=admin
user=user
guest=guest
roles.properties
admin=staff
user=customer
guest=guest
Die entsprechenden Business Methoden werden declarativ im Deployment Descriptor ejbjar.xml definiert:
ejbejb-jar.xml
<method-permission>
<role-name>customer</role-name>
<method>
<ejb-name>EJBSecurity</ejb-name>
<method-intf>Home</method-intf>
<method-name>*</method-name>
</method>
<method>
<ejb-name>EJBSecurity</ejb-name>
<method-intf>Remote</method-intf>
<method-name>doSomething</method-name>
</method>
</method-permission>
18.10.2 prommatische Security
Die programmatische Sicherheit kommt zusätzlich im Sourcecode zur Geltung:
Business Methode doSomething der SecurityTestBean.java
public String doSomething(String role) throws FailedLoginException {
Principal p = sc.getCallerPrincipal();
if (sc.isCallerInRole(role)) {
return "Principal (" + p + ") identifies as Role \"" + role + "\"";
} else {
throw new FailedLoginException("wrong Principal / wrong Role");
}
}
Output:
Principal : admin wants to getPrincipalInfo : Principal = admin Role(staff) = true
Principal : admin wants to doSomething : Principal (admin) identifies as Role "staff"
© 2008 Stephan Metzler
J2EE Security
Version 3.0
Seite 111 von total 218 Seiten
J2EE – EJB 2.x
19
Transaktionen
Allgemein der kleinste, unteilbare und daher an einem Stück ununterbrochen abzuarbeitende
Prozess einer Anwendung. Transaktionen werden daher vollständig oder gar nicht abgearbeitet.
Speziell ist häufig eine logisch zusammenhängende Folge von Operationen in einer Datenbank
gemeint, die die Datenbank von einem konsistenten in einen weiteren konsistenten Zusand
überführt.
19.1
Transaktionskonzepte
Die vier Eigenschaften von Transaktionen bezeichnet man als ACID-Prinzip:
ACID
Atomicity (Atomarität = unteilbar): Eine Transaktion wird entweder vollständig oder gar nicht
ausgeführt. Resultate teilweise ausgeführter Transaktionen sind nie nach aussen sichtbar.
Consistency (Konsistenz = zusammenhaltend): Eine Transaktion bewirkt immer den Übergang
von einem konsistenten Datenbankzustand in einen anderen konsistenten Datenbankzustand.
Falls durch eine Transaktion Integritätsbedingungen verletzt werden, wird die Transaktion
abgebrochen und die Datenbank im Ursprungszustand belassen.
Isolation (Isolation = getrennt): Parallel ablaufende Transaktionen sehen nichts von Parallelität.
Jede Transaktion sieht während ihrer Ausführung stets nur einen konsistenten, während der
Transaktion von aussen unveränderten, Datenbankzustand.
Durability (Dauerhaftigkeit = persistent): Sobald eine Transaktion beendet ist, ist der von ihr
bewirkte Zustandsübergang dauerhaft. Die durch die Transaktion vorgenommenen Änderungen
können nicht mehr verschwinden.
19.1.1
Lokale und verteilte Transaktionen
Transaktionen, bei deren Durchführung nur lokale Daten betroffen sind und nicht mehrere
Ressourcen verwendet werden müssen, werden als lokale Transaktionen (local transactions)
bezeichnet. Wenn dagegen mit mehreren Daten, die auf verteilten Systemen liegen, gearbeitet
wird, spricht man von einer verteilten Transaktion (distributed transaction) oder einer globalen
Transaktion (global transaction). Um eine globale Transaktion durchzuführen, muss sie in Teiltransaktionen aufgeteilt werden.
In dem Sequenzdiagramm startet die Applikation
(BOT = begin of transaction) zunächst eine
Teiltran
Teiltransaktion
transaktion 1 auf der ersten Datenbank und ändert
den Wert A.
A Von Teiltransaktion 1 erfolgt eine positive
Rückmeldung. Daraufhin startet die Applikation eine
zweite Teiltransaktion auf der zweiten Datenbank und
ändert ebenfalls den Wert A,
A worauf sie auch hier eine
positive Rückmeldung erhält. Nun kann an die erste
Datenbank ein Commit übermittelt werden und die
Teiltransaktion 1 ist beendet. Das Gleiche geschieht
mit der zweiten Teiltransaktion, und damit wurde die
gesamte Transaktion positiv abgeschlossen.
© 2008 Stephan Metzler
Transaktionen
Version 3.0
Seite 112 von total 218 Seiten
J2EE – EJB 2.x
19.1.2
Zwei Phasen Commit
Allerdings kann es zu inkonsistenten Zuständen
kommen, falls die Datenbank, die für Teiltransaktion 2
zuständig ist, vor dem Empfang der zweiten CommitAnweisung ausfällt. Beim Neustart der Datenbank wird
diese die Teiltransaktion 2 und damit auch den Wert A
zurücksetzen, weil sie nicht korrekt beendet wurde.
Dadurch haben die Datenbanken unterschiedliche
Daten für Wert A.
Um solche Fehlerfälle abzufangen, bedarf es
erweiteter Protokolle, z.B. des Zwei-Phasen-CommitProtokolls (2PC-Protokoll).
In der ersten Phase teilt die Applikation den
Ressourcen der Teiltransaktionen mit, dass sie sich
vorbereiten sollen ("Prepare-to-Commit"). Wenn die
Antworten ("Ready to Commit") aller Knoten positiv
waren, wird die zweite Phase eröffnet, und die
Applikation sendet ein Commit an die Knoten ("Commit
Phase").
19.2
Java Transaction API (JTA) und Java Transaction Service (JTS)
Java Transaction Service (JTS) spezifiziert die Implementierung eines Transaktionsmanagers,
der die Java Transaction API als Programmierschnittstelle anbietet und JTA die
Programmierschnittstelle. Damit können Programmierer, Applikationsserver und
transaktionsunterstützende Ressourcen den Dienst verwenden. Für jede
transaktionsunterstützende (XA-kompatible) Ressource wird ein Ressourcenmanager
eingesetzt.
Die Koordination von Transaktionen über die Grenzen eines Ressourcenmanagers hinaus wird
durch den Transaktionsmanager realisiert und hat nun die folgenden Aufgaben:
- Initiierung von Start, Abbruch und Abschluss von Transaktionen.
- Über Transaktionsmanager erfolgt die Realisierung globaler, verteilter Transaktionen. Für
eine globale Transaktion werden die beteiligten Ressourcen von dem Transaktions-manager
verwaltet.
- Der erfolgreiche Abschluss globaler Transaktionen wird mittels des Zwei-Phasen-CommitProtokolls koordiniert.
- Die Kooperation eines Transaktionsmanagers mit anderen Transaktionsmanagern kann über
CORBA erfolgen.
© 2008 Stephan Metzler
Transaktionen
Version 3.0
Seite 113 von total 218 Seiten
J2EE – EJB 2.x
Der Zugriff auf einen Java Transaction Service erfolgt mit der Java Transaction API, die aus
drei Schnittstellen besteht:
javax.transaction.UserTransaction:
javax.transaction.UserTransaction Mit diesem Interface können Transaktionen gestartet
begin(), abgebrochen rollback() oder abgeschlossen commit() werden. Zusätzlich
kann man noch den Status einer Transaktion abfragen getStatus(), ein Timeout setzen
setTransactionTimeout() oder sicherstellen, dass mit einem Rollback
setRollbackOnly() die Transaktion auf jeden Fall beendet wurde.
javax.transaction.TransactionManager:
javax.transaction.TransactionManager Ausser den Methoden der UserTransaction ermöglicht
dieses Interface einem Applikationsserver noch zwei zusätzliche Methoden: Mit suspend()
kann eine Transaktion vorübergehend angehalten und mit resume() wieder aufgenommen
werden.
javax.transaction.Transaction und javax.transaction.xa.XAResource:
javax.transaction.xa.XAResource Auch in der TransaktionSchnittstelle wurden alle Methoden aus UserTransaction implementiert. Ausserdem besteht
noch die Möglichkeit zum Registrieren enlistResource() und zum Deregistrieren einer
Ressource delistResource() innerhalb der Transaktion; enlistResource() erhält
als Parameter einerseits die Ressource selbst – als Objekt, das die XAResource-Schnittstelle
implementiert – und andererseits einen als Objekt typisierten Parameter, der den
Ressourcenmanager identifiziert und mit diesem interagiert, um das Zwei-Phasen-Commit
durchzuführen. Die XARessource-Schnittstelle ist ein Java-Mapping des XA-Standards nach
der X/Open-Spezifikation. Die wichtigsten Methoden dieser Schnittstelle sind:
- der Beginn der Ausführung mit der Zuordnung zu einer globalen Transaktion durch Aufruf
von start()
- das Einholen der Entscheidung über ein Commit mittels prepare()
- das Beenden der Ausführung mit end()
- das Abschliessen / Zurücksetzen der Transaktion mit commit() / rollback()
TransaktionsManager
Java Client Anwendung
oder Applikationsserver
JDBC
JMS
RessourcenManager
RessourcenManager
RessourcenManager
© 2008 Stephan Metzler
Transaktionen
DB
DB
JMSServer
Version 3.0
Seite 114 von total 218 Seiten
J2EE – EJB 2.x
19.3
User-Transaktionen
Um sicherzustellen, dass die Transaktionen einer Applikation den ACID-Prinzipien folgen, sind
sorgfältiges Entwerfen und Implementieren nötig. Jede nachträgliche Änderung kann teuer
werden. Um die Implementierung zu unterstützen, hat die EJB-Spezifikation zwei Typen von
Transaktionsverwaltung vorgesehen:
- implizite (container-managed transactions) Transaktionssteuerung
- explizite (bean-managed transactions) Transaktionssteuerung
In allen EJBeans ausser den Entity-Beans kann zwischen den beiden Transaktionssteuerungen
gewählt werden. Bei den beiden Entity-Beans ist seit der Spezifikation 2.0 nur noch die implizite Transaktionssteuerung möglich:
Wenn eine Entity-Bean in einer Client-Transaktion aufgerufen wird, werden erst einmal mit
ejbLoad die Daten von der Datenbank geladen. Es werden entsprechende Locks in der
Datenbank gesetzt und die Entity-Bean mit den Werten der Datenbank "gefüllt". Dann werden
ein oder mehrere Geschäftsmethoden aufgerufen, und dann wird die Client-Transaktion
"commited". Daraufhin ruft der Container ejbStore auf, um die Datenbank mit den neuen
Werten zu aktualisieren. Die Locks in der Datenbank werden wieder entfernt. Die Transaktion
in der Entity-Bean sollte also die ejbLoad, die Geschäftsmethoden und ejbStore
umfassen. Wenn nun Bean-Managed-Transactions (BMT) verwendet werden würde, müsste in
ejbLoad mit begin() die Transaktion begonnen und in ejbStore mit commit()
beendet werden. Das Problem dabei ist, dass der Container die Methoden aufruft und nicht der
Client. Dadurch kann nicht garantiert werden, dass die Methoden auch tatsächlich in dieser
Reihenfolge aufgerufen werden. Es könnten mehr Transaktionen begonnen als beendet
werden. Infolge der Container-Verwaltung sind BMT in Entity-Beans nicht erlaubt.
Das Element <transaction-type> im Deployment-Deskriptor legt fest, welche Transaktionsverwaltung verwendet werden soll. Der Wert ist entweder Bean oder Container.
19.3.1
Bean Managed Transaction
Mit Hilfe der BMT, ist es möglich, Transaktionen explizit zu steuern. Dies kann umfassen:
betrifft Transaktionen sowohl mit anderen EJBeans, als auch mit Datenbanken über JDBC.
- Transaktionen mit anderen EJBeans
- Transaktionen mit Datenbanken über JDBC
Es kann nötig sein, die Transaktionssteuerung selbst zu übernehmen, wenn z.B. einige
Ressourcen nicht vom Transaktions-Manager gesteuert werden können. BMT können jedoch
keine verschachtelten Transaktionen verwalten. Um die explizite Transaktionssteuerung zu
ermöglichen, unterstützt EJB die Java Transaction API (JTA). Die darin enthaltene Klasse
UserTransaction stellt eine Schnittstelle zum Transaktions-Manager zur Verfügung, mit
der der Gültigkeitsbereich einer Transaktion verwaltet werden kann. In dieser Schnittstelle
können folgende Methoden aufgerufen werden:
© 2008 Stephan Metzler
Transaktionen
Version 3.0
Seite 115 von total 218 Seiten
J2EE – EJB 2.x
- begin() – leitet eine neue Transaktion ein.
Der Thread, der die neue Transaktion erzeugt hat, wird mit dieser verbunden. Falls innerhalb
dieser Transaktion Beans verwendet werden, die bereits existierende Transaktionen
unterstützen, wird die Transaktion an sie weitergereicht.
- commit() – beendet die aktuelle mit diesem Thread verbundene Transaktion.
- rollback() – Transaktion wieder zurückführen
Um im Falle eines Fehlers die Transaktion wieder zurückzuführen und die schon
durchgeführten Aktualisierungen rückgängig zu machen.
- setRollBackOnly() – merkt die Transaktion für das Zurückführen vor.
Die Transaktion wird nach ihrer Beendigung in jedem Fall zurückgeführt, ob sie nun
erfolgreich war oder nicht.
- setTransactionTimeout(int seconds) – Lebenszeit festlegen
Mit dieser Methode wird festgelegt, wie lange die Transaktion existieren darf, bevor ein
Timeout auftritt. Falls diese Methode nicht verwendet oder der Wert 0 übergeben wird, gilt
der Default-Wert des Transaktions-Managers. Diese Methode muss unmittelbar nach
begin() aufgerufen werden.
- getStatus() – Abfrage des aktuellen Transaktions-Status
Liefert einen Integer-Wert zurück, der mit denen in der Schnittstelle javax.transaction.Status
definierten Konstanten verglichen werden kann.
Um eine BMT verwenden zu können, wird das entsprechende <transaction-type> Tag mit Bean
beschrieben.
<transaction-type>Bean</transaction-type>
Dann wird eine Verbindung zum Transaktions-Manager des EJB-Server hergestellt und über
den Container ein UserTransaction-Objekt geholt.
Context jndiContext = new InitialContext();
UserTransaction trans = (UserTransaction)
jndiContext.lookup("java:comp/UserTransaction");
...
oder:
SessionContext ejbContext;
UserTransaction trans = ejbContext.getUserTransaction();
trans.begin();
...
trans.commit();
<interface> UserTransaction:
interface UserTransaction {
void begin();
void setTransactionTimeout(int seconds);
void commit();
void rollback();
void setRollbackOnly();
int getStatus();
}
Die möglichen Werte der Methode getStatus finden sich als Konstanten in der Schnittstelle
javax.transaction.Status.
© 2008 Stephan Metzler
Transaktionen
Version 3.0
Seite 116 von total 218 Seiten
J2EE – EJB 2.x
19.3.2
Container-Managed-Transactions
Mit Hilfe der CMT ist es möglich, die Steuerung der Transaktionen dem Container zu
überlassen. Der Vorteil ist, dass kein zusätzlicher Code in die Geschäftslogik geschrieben
werden muss und die Bean dadurch übersichtlicher wird. Alles was sich innerhalb eines trycatch-Blocks befindet, gehört zur selben Transaktion. Falls bei der Ausführung des Blocks
ein Fehler auftritt, werden alle schon durchgeführten Anweisungen rückgängig gemacht. Diese
Methode hat allerdings den Nachteil, dass keine verschachtelten Transaktionen möglich sind.
Ein weiterer Aspekt ist, dass das Verhalten der Bean geändert werden kann, ohne die Implementierung ändern zu müssen. Zusätzlich wird das Risiko einer fehlerhaften Verwendung von
Transaktions-Protokollen wie JTA reduziert. Das Transakionsverhalten kann über den
Deployment-Deskriptor gesteuert werden, der die Transaktions-Attribute für die gesamte Bean
oder für jede Bean-Methode einzeln festlegt. Ein Beispiel soll den Unterschied verdeutlichen:
CMT auf Bean Ebene
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>AdressBean</ejb-name>
<!-- Hiermit werden alle Methoden angesprochen -->
<method-name>*</method-name>
</method>
<!-- Jetzt kommt das Transaktions-Attribut.
Es kann die Werte "NotSupported", "Supports", "Required", "RequiresNew",
"Mandatory" oder "Never" bekommen. -->
<!-- Alle Methoden bekommen das Transaktions-Attribut "Required". -->
<trans-attribute>Required</trans-attribute>
...
Wie man dagegen einzelne Methoden unterschiedlich konfigurieren kann, soll der folgende
Ausschnitt aus einem Deployment-Descriptor zeigen:
CMT auf Methoden Ebene
<container-transaction>
<method>
<ejb-name>AdressBean</ejb-name>
<method-name>getVorname</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>AdressBean</ejb-name>
<method-name>getVorname</method-name>
<method-param>format</method-param>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
...
Mit dem Eintrag <method-name> können überladenen Methoden unterschiedliche Transaktions-Attribute zugewiesen werden:
- Required: Die Bean soll immer in einer Transaktion laufen.
Falls schon eine Transaktion läuft, wird die Methode ein Teil von ihr, andernfalls wird eine
© 2008 Stephan Metzler
Transaktionen
Version 3.0
Seite 117 von total 218 Seiten
J2EE – EJB 2.x
-
-
-
-
-
neue Transaktion vom Container gestartet. Die neue Transaktion umfasst allerdings nur die
Required-Bean und die von ihr benutzten Beans. Wenn die Required-Methode beendet ist,
wird auch die Transaktion beendet.
RequiresNew: Es wird immer eine neue Transaktion gestartet.
Wenn der aufrufende Client bzw. Bean selbst zu einer Transaktion gehört, wird diese
ausgesetzt, bis die "RequiresNew"-Methode beendet ist.
Supports: Die Methode wird zur Transaktion des Aufrufers einbezogen.
Jedoch nur wenn dieser eine Transaktion hat. Falls der Aufrufer zu keiner Transaktion
gehört, wird auch keine neue Transaktion vom Container erzeugt.
Mandatory: Diese Bean-Methode gehört immer zur Transaktion des aufrufenden Clients.
Das entspricht dem Gegenteil des Never-Attribut. Falls der aufrufende Client mit keiner
Transaktion verbunden ist, tritt eine Exception auf.
NotSupported: Methoden können keine Transaktionen unterstützen oder erzeugen.
Falls der aufrufende Client Teil einer Transaktion ist, wird diese Transaktion während der
Ausführung der Methode ausgesetzt (unterbrochen).
Never: Die Methode darf nicht in eine Transaktion einbezogen werden.
Falls ein Client innerhalb einer Transaktion diese Methode aufrufen sollte, tritt eine Exception
auf. Wenn man also vermeiden möchte, dass die Clients, die diese Methode aufrufen,
Transaktionen verwenden, dann kann man das mit diesem Attribut erreichen.
Allerdings lassen sich nicht alle Transaktions-Attribute mit allen Beans nutzen. Die folgende
Tabelle zeigt, welche Kombinationen möglich sind:
Stateless
Session
Bean
Stateful
Session
Bean
Entity
Bean
Message
Driven
Bean
Required
Ja
Ja
Ja
Ja
RequiresNew
Ja
Ja
Ja
Nein
Mandatory
Mandatory
Ja
Ja
Ja
Nein
Supports
Ja
Nein
Nein
Nein
NotSupported
Ja
Nein
Nein
Ja
Never
Ja
Nein
Nein
Nein
TransaktionsTransaktions-Attribut
Für CMP EntityBeans ist es sinnvoll nur Required,
Required RequiresNew oder Mandatory als
Transaktionsattribut zu verwenden, damit die Datenbankzugriffe zuverlässig bleiben.
Auf einer Collection, die von einem HomeInterface einer CMP Enitity-Bean geliefert wird, darf
man nur innerhalb einer Transaktion zugreifen, und zwar derjenigen, in der sie erzeugt wurde.
Für solche Methoden des HomeInterface soll man daher nicht RequiresNew als
Transaktionsattribut verwenden.
Beim Auftreten einer Exception wird eine laufende Transaktion abgebrochen. Darunter versteht
man eine RuntimeException (insbesondere EJBException). Ausserdem vernichtet in diesem Fall
der EJB-Container die Bean, weil er sich nicht mehr sicher ist, dass ihr Inhalt noch brauchbar
ist. Der Aufrufer erhält eine RemoteException bzw. EJBException, die in irgendeiner Form als
Fehlermeldung ausgegeben wird.
Alle Ausnahmen ausser RuntimeExceptions und RemoteExceptions (einschliesslich
Unterklassen) zählen als ApplicationExceptions. Sie werden an den Aufrufer weitergegeben.
Wenn möglich, wird die Transaktion fortgeführt. Ob dies nicht gelungen ist, kann man mit dem
© 2008 Stephan Metzler
Transaktionen
Version 3.0
Seite 118 von total 218 Seiten
J2EE – EJB 2.x
Methodenaufruf context.getRollbackOnly() herausfinden; context ist vom Typ
EJBContext. Mit context.setRollbackOnly() kann man den erfolglosen Abbruch der
laufenden Transaktion erzwingen.
19.3.3
JDBC Transaktionen
Die JDBC-Transaktionen sind Transaktionen, die vom Transaktions-Manager des Datenbankmanagementsystems (DBMS) verwaltet werden. Mit Hilfe der java.sql.ConnectionSchnittstelle ist es möglich, Datenbank-Transaktionen selbst zu steuern. Der standardmässig
eingeschaltete AutoCommit-Modus sollte vorher mit setAutoCommit(false)
ausgeschaltet werden. Der Beginn einer Transaktion erfolgt implizit mit der ersten SQLAnweisung. Es können dann weitere SQL-Anweisungen folgen, bis schliesslich die gesamte
Transaktion mit commit() bestätigt oder mit rollback() zurückgenommen wird.
Beispiel
con.setAutoCommit(false);
stmt.executeUpdate("INSERT INTO Tabelle
if (error) {
con.rollback();
} else {
con.commit();
}
© 2008 Stephan Metzler
Transaktionen
VALUES (Wert1, Wert2, ...);");
Version 3.0
Seite 119 von total 218 Seiten
J2EE – EJB 2.x
19.4
Übung ee10 : EJB Transaction
Wie bereits erwähnt laufen alle Methodenaufrufe einer Enterprise Bean innerhalb einer
Transaktion. Normalerweise erstreckt sich die Transaktion sogar über entfernte
Methodenaufrufe (nicht aber auf die Benutzung von Webdiensten). Wenn eine Methode eine
Exception auslöst (bzw. eine Nachricht, als Folge davon, nicht bestätigt wird), wird die
Transaktion erfolglos abgebrochen (rollback). Nur wenn alles gut geht, wird die Transaktion
erfolgreich abgeschlossen (commit).
Das liegt daran, dass wir im Deployment-Descriptor bisher immer Folgendes geschrieben
haben:
<assembly-descriptor>
...
<container-transaction>
...
<trans-attribute>Required</trans-attribute>
</container-transaction>
...
</assembly-descriptor>
Insgesamt gibt es folgende mögliche Werte für das Tag <trans-attribute>:
Never : Die Methoden dürfen nicht im Rahmen einer Transaktion aufgerufen werden.
NotSupported : Es ist egal, ob die Methoden im Rahmen einer Transaktion aufgerufen werden.
In jedem Fall werden die Methodenaufrufe ausserhalb einer Transaktion abgewickelt.
Supports : Falls die Methoden im Rahmen einer Transaktion aufgerufen werden, werden diese
Transaktionen weitergeführt, andernfalls werden die Methodenaufrufe eben ausserhalb einer
Transaktion abgewickelt.
Required : Falls die Methoden im Rahmen einer Transaktion aufgerufen werden, werden diese
Transaktionen weitergeführt, andernfalls wird eine Transaktion begonnen.
RequiresNew : Es ist egal, ob die Methoden im Rahmen einer Transaktion aufgerufen werden.
In jedem Fall wird eine (Unter-)Transaktion begonnen. (Dies kann ziemlich aufwendig werden.)
Mandatory : Die Methoden müssen im Rahmen einer Transaktion aufgerufen werden.
19.4.1
Transaktionssteuerung
Erstellen Sie eine Bankkontenverwaltung mit einem BMP Entity Bean, das einen Übertrag von
einem Konto A zu einem Konto B ermöglicht. Negative Saldos auf einem der Konten soll dabei
mit einer entsprechenden Transaktionssteuerung unterbunden werden.
Ein Konto definiert sich durch eine "id" einem "owner" und einer "balance" .
mysql> desc accounts;
+---------+-------------+------+-----+---------+-------+
| Field
| Type
| Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id
| char(3)
|
| PRI | 0
|
|
| owner
| varchar(50) | YES |
| NULL
|
|
| balance | double
| YES |
| NULL
|
|
+---------+-------------+------+-----+---------+-------+
© 2008 Stephan Metzler
Transaktionen
Version 3.0
Seite 120 von total 218 Seiten
J2EE – EJB 2.x
SQL Skript : accounts Table
# MySQL-Front Dump 2.2
# Host: localhost
Database: ee
#-------------------------------------------------------# Server version 4.1.1a-alpha-nt
USE ee;
# Table structure for table 'accounts'
DROP TABLE IF EXISTS accounts;
CREATE TABLE `accounts` (
`id` char(3) NOT NULL default '0',
`owner` varchar(50) default NULL,
`balance` double default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
# Dumping data for table 'accounts'
INSERT INTO accounts VALUES("1","Client 1","100");
INSERT INTO accounts VALUES("2","Client 2","200");
Ansatz: BankClient.java
final Bank bank = home.create();
final Account a1 = bank.getAccount("1");
final Account a2 = bank.getAccount("2");
printAccount(a1);
printAccount(a2);
final double amount = 100;
Thread t1 = new Thread() {
public void run() {
try {
System.out.println("Thread started : transfer 1 -> 2
bank.transfer(a1, a2, amount);
System.out.println("Thread finished");
} catch (Exception e) {
e.printStackTrace();
}
}
};
: " + amount);
t1.start();
Thread.sleep(2000);
printAccount(a1);
printAccount(a2);
Output:
ID : 1
OWNER : Client 1 BALANCE
ID : 2
OWNER : Client 2 BALANCE
Thread started : transfer 1 -> 2
Thread finished
ID : 1
OWNER : Client 1 BALANCE
ID : 2
OWNER : Client 2 BALANCE
© 2008 Stephan Metzler
Transaktionen
: 100.0
: 200.0
: 100.0
: 0.0
: 300.0
Version 3.0
Seite 121 von total 218 Seiten
J2EE – EJB 2.x
Wird bei einem Übertrag der Saldo des "Client 1" kleiner als Null so soll die Transaktion nicht
durchgeführt und die bereits ausgeführten Datenänderungen rückgängig gemacht werden:
Ansatz: BankBean.java (Fragment)
public class BankBean implements SessionBean {
private SessionContext context;
private AccountHome accountHome;
public Account getAccount(String number) {
log();
try {
return accountHome.findByPrimaryKey(number);
} catch (Exception e) {
throw new EJBException(e.getMessage());
}
}
public void transfer(Account from, Account to, double amount)
throws IllegalOperationException {
log();
try {
to.deposit(amount);
from.withdraw(amount);
} catch (IllegalOperationException e) {
context.setRollbackOnly();
} catch (Exception e) {
throw new EJBException(e.getMessage());
}
…
}
Ansatz: AmountBean.java
AmountBean.java (Fragment)
public class AccountBean implements EntityBean {
public void deposit(double amount) throws IllegalOperationException {
log();
balance += amount;
if (balance < 0)
throw new IllegalOperationException("balance becomes less than 0");
}
public void withdraw(double amount) throws IllegalOperationException {
log();
balance -= amount;
if (balance < 0)
throw new IllegalOperationException("balance becomes less than 0");
}
…
}
Ansatz: IllegalOperationException.java
package beans;
public class IllegalOperationException extends Exception {
public IllegalOperationException(String reason) {
System.out.println(reason);
}
}
© 2008 Stephan Metzler
Transaktionen
Version 3.0
Seite 122 von total 218 Seiten
J2EE – EJB 2.x
Testen Sie den SampleCode und stellen Sie sicher, dass die Transaktionssteuerung
funktioniert.
Die Konsolestatements (LogDatei) des Applikationsservers erläutern die Sequenz:
file:///[jboss.home]/server/ee/log/server.log
17:24:33,234
17:24:33,234
17:24:33,234
17:24:33,234
17:24:33,250
17:24:33,250
17:24:33,265
17:24:33,265
17:24:33,359
17:24:33,359
17:24:33,359
17:24:33,375
17:24:33,375
17:24:33,390
17:24:33,390
17:24:33,406
17:24:33,406
17:24:33,406
17:24:33,406
17:24:33,421
17:24:33,437
17:24:33,453
17:24:33,453
17:24:33,453
17:24:33,468
17:24:33,468
17:24:33,500
17:24:33,500
17:24:33,500
17:24:33,500
17:24:35,484
17:24:35,484
...
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
beans.BankBean@14bfb68[16604072]: setSessionContext
beans.BankBean@14bfb68[16604072]: ejbCreate
beans.BankBean@14bfb68[16604072]: getAccount
beans.AccountBean@1667cff[16604072]: <init>
beans.AccountBean@1667cff[16604072]: setEntityContext
beans.AccountBean@1667cff[16604072]: makeConnection
beans.AccountBean@1667cff[16604072]: ejbFindByPrimaryKey
beans.AccountBean@1667cff[16604072]: selectByPrimaryKey
beans.BankBean@14bfb68[16604072]: getAccount
beans.AccountBean@1667cff[16604072]: ejbFindByPrimaryKey
beans.AccountBean@1667cff[16604072]: selectByPrimaryKey
beans.AccountBean@1667cff[16604072]: ejbActivate
beans.AccountBean@1667cff[16604072]: ejbLoad
beans.AccountBean@1667cff[16604072]: getOwner
beans.AccountBean@1667cff[16604072]: ejbStore
beans.AccountBean@1667cff[16604072]: getBalance
beans.AccountBean@1667cff[16604072]: ejbStore
beans.AccountBean@1881bb1[16604072]: <init>
beans.AccountBean@1881bb1[16604072]: setEntityContext
beans.AccountBean@1881bb1[16604072]: makeConnection
beans.AccountBean@1881bb1[16604072]: ejbActivate
beans.AccountBean@1881bb1[16604072]: ejbLoad
beans.AccountBean@1881bb1[16604072]: getOwner
beans.AccountBean@1881bb1[16604072]: ejbStore
beans.AccountBean@1881bb1[16604072]: getBalance
beans.AccountBean@1881bb1[16604072]: ejbStore
beans.BankBean@14bfb68[16604072]: transfer
beans.AccountBean@1881bb1[16604072]: deposit
beans.AccountBean@1667cff[16604072]: withdraw
balance becomes less than 0
beans.AccountBean@d4e99c[16604072]: <init>
beans.AccountBean@d4e99c[16604072]: setEntityContext
Testen und überprüfen Sie anhand der Methoden-Transaktionsattribute das transaktionale
Verhalten der EJB Applikation:
Klasse
BankBean
Methoden
deposit()
transfere()
withdraw()
getBalance()
getOwner()
Required
Required
Required
Required
Required
RequiresNew
Required
Supports
Required
Supports
Transaktionsattribut Required
Supports
NotSupported
Required
Supports
Required
NotSupported
Required
Required
NotSupported
NotSupported
NotSupported
© 2008 Stephan Metzler
Transaktionen
AccountBean
TransaktionsTransaktionsVerhalten
Version 3.0
Seite 123 von total 218 Seiten
J2EE – EJB 2.x
20
Message Driven Beans
Die Session-Bean und die Entity-Bean können als Remote-Procedure-Call-basierte
Komponenten angesehen werden, die immer synchron vom Client aufgerufen werden. Die
Message-Driven-Bean dagegen unterstützt die asynchrone Kommunikation. Der Client muss
nun nicht mehr warten, bis der Server die Aufgabe erledigt hat, sondern kann gegebenenfalls
sofort mit seiner Arbeit fortfahren. Um dies zu realisieren, wird von der Message-Bean keine
direkte Schnittstelle zur Verfügung gestellt, sondern vom Client eine Nachricht an einen
Message-Server (Nachrichten-Server) geschickt. Sie wird vom Container aufgerufen, wenn
eine Nachricht für sie eintrifft. Bei Inbetriebnahme (deployment) der Bean auf dem Server wird
daher bestimmt, auf welche Nachrichten die Message-Driven-Bean reagieren soll (subscribe).
Was sie dann machen soll, wird in der onMessage-Methode festgelegt.
20.1
JMS
Der Java Message Service (JMS) von Sun ist eine API für den Zugang zu unternehmensweiten
Messaging-Systemen. JMS definiert eine Schnittstelle, legt aber die Implementierung nicht
fest. Der Vorteil für Software-Hersteller besteht darin, dass sie ihre existierenden MessagingProdukte mit JMS-Schnittstellen versehen können, ohne sie wesentlich ändern zu müssen.
Bei der Integration von EJB mit JMS spielt die Message-Driven-Bean die zentrale Rolle. Da sie
weder Remote- noch Home-Interface hat, greift der Client per JMS auf diese Bean zu. Er
findet sie indirekt über die Auswahl einer Message-Queue (Point-to-Point-Verbindung) oder
eines Message-Topics (Publish/Subscribe-Mechanismus) mittels JNDI (Java Naming and
Directory Service). Der Deployment Descriptor (DD) legt fest, für welches der beiden
Messaging-Modelle die Message-Driven-Bean zuständig ist. Den Vertrag für letztere schliessen
Bean Provider und Container Provider folgendermassen: Ersterer programmiert die MessageDriven-Bean und implementiert die Business-Methode - die fachliche Logik - in der
onMessage()-Methode. Letzterer aktiviert die MDB und ruft die genannte Methode bei
Eintreffen einer Message auf. Die Message-Driven-Bean ermöglicht den asynchronen Aufruf
von EJBeans und realisiert nebenläufige Nachrichten. EJBeans lassen sich nun wie jeder
andere JMS Message Listener ansprechen.
Nachrichten erzeugen mit der JMS API
Das Erzeugen und Absenden einer
Nachricht über JMS erfolgt in sechs
Schritten.
1.
Treiber lokalisieren
2.
Verbindung erzeugen
3.
Session erzeugen
4.
Destination lokalisieren
5.
Consumers / Producers erzeugen
6.
Nachricht senden / empfangen
© 2008 Stephan Metzler
Message Driven Beans
Version 3.0
Seite 124 von total 218 Seiten
J2EE – EJB 2.x
Lokalisieren des JMSJMS-Treibers:
Zunächst muss ein Treiber für das verwendete JMS-Produkt geladen werden. Dies geschieht
durch ein lookup(CONNECTION_FACTORY), wobei CONNECTION_FACTORY der JNDIName des Treibers ist, der die Schnittstelle ConnectionFactory implementiert.
Erzeugen der JMSJMS-Verbindung:
Eine JMS-Verbindung ist eine aktive Verbindung zu einem JMS-Provider, der die NetzwerkKommunikation kapselt. Analog zu JDBC wird der Treiber verwendet, um eine Connection
anzufordern. Es gibt zwei Connection-Klassen, TopicConnection und
QueueConnection.
Erzeugen einer JMS Session:
Session:
Eine JMS Session ist ein Hilfsobjekt, das verwendet wird, um Nachrichten zu senden oder zu
empfangen. Es dient dazu, um die Eigenschaften des Nachrichtenaustauschs festzulegen und
in eine Transaktion einzubeziehen. Es gibt zwei JMS Session Klassen, TopicSession und
QueueSession.
Lokalisieren einer JMS Destination:
Destination:
Eine JMS Destination ist ein Kommunikationskanal zu dem Nachrichten gesendet oder von
dem Nachrichten empfangen werden können. Die Auswahl des Kanals erfolgt über ein
lookup(CHANNEL), wobei CHANNEL der JNDI-Name des Kanals ist, der beim Server
eingetragen wurde.
Erzeugen eines JMS Consumers oder eines JMS Producers:
Producers:
Senden oder Empfangen der Nachricht:
Falls eine Nachricht verschickt werden soll, muss sie erst noch erstellt werden. Zu diesem
Zweck gibt es mehrere Typen von Nachrichten: Text, Bytes, Streams, Objects und Maps.
Nachdem die Nachricht instantiiert wurde, kann sie über den Producer gesendet werden. Falls
eine Nachricht über den Consumer empfangen wurde, muss sie dem Typ entsprechend
verarbeitet werden.
Dieser gesamte Ablauf ist bei beiden Kommunikationsmodellen gleich. Es gibt für jedes
Interface zwei Ausprägungen analog zu den beiden Kommunikationsmodellen.
ErzeugerErzeuger-Interface
PointPoint-toto-Point
Publish / Subscribe
ConnectionFactory
QueueConnectionFactory
TopicConnectionFactory
Connection
Queue Connection
TopicConnection
Destination
Queue
Topic
Session
QueueSession
TopicSession
MessageProducer
QueueSender
TopicPublisher
MessageConsumer
QueueReciever, -Browser
TopicSubscriber
© 2008 Stephan Metzler
Message Driven Beans
Version 3.0
Seite 125 von total 218 Seiten
J2EE – EJB 2.x
20.2
MDB
Eine Message-Driven-Bean (MDB) ist eine EJB Komponente, die ausschliesslich dem Empfang
von Nachrichten dient, welche über Queues oder Topics eines JMS-Servers gesendet wurden.
Die Eigenschaften einer MDB lassen sich wie folgt zusammenfassen:
Im Gegensatz zu den anderen EJBeans besitzt die MDB kein Remote, Home, Local-Home
oder Local-Interface. Sie wird ausschliesslich über Nachrichten verwendet.
Die MDB hat nur eine einzige Geschäftsmethode. Die Methode onMessage(Message)
wird immer dann vom Container aufgerufen, wenn eine Nachricht für die MDB eingetroffen ist.
Sie akzeptiert eine JMS Message. Standardmässig sollte in dieser Methode zuerst geprüft
werden, ob auch die richtige Nachricht eingetroffen ist.
Eine MDB liefert keine Rückgabewerte oder Exceptions. Falls der Client Rückgabewerte
benötigt, müssen diese wiederum als Nachricht abgeschickt werden. Dies macht der Container
aber nicht automatisch, sondern muss in der Geschäftsmethode implementiert werden. Dies gilt
ebenso für den Fehlerfall. Natürlich kann ein Systemfehler auftreten, dieser wird aber nur vom
Container registriert.
Eine MDB ist zustandslos und verhält sich wie eine Stateless-Session-Bean.
Eine MDB kann ein permanenter (durable) oder nicht-permanenter (non-durable) Empfänger
sein. Als permanenter Empfänger kann die MDB selbst dann die Nachrichten empfangen, die
an einen Topic gesendet wurden, wenn sie nicht aktiv war. Der JMS-Server speichert die
Nachrichten und überträgt sie, wenn die MDB wieder aktiv ist. Als nicht-permanenter
Empfänger gehen die Nachrichten dagegen verloren.
20.2.1
Bean Life Cycle
Container initiated:
ejbRemove()
does not exist
Methode-Ready-Pool
Container initiated:
setMessageDrivenContext
ejbCreate()
Container initiated:
onMessage()
setSessionContext Die Methode setMessageDrivenContext(MessageDrivenContext)
wird vor ejbCreate ausgeführt, um der Message-Driven-Bean einen
Zugang zu den Informationen über die Laufzeit-Umgebung zu geben.
ejbCreate
Die Methode ejbCreate der Message-Driven-Bean wird vom
Container ausgeführt. In dieser Methode sollen Verbindungen (JNDI) zu
Ressourcen und Referenzen anderer EJ-Beans initialisiert werden.
Datenbankverbindungen sollten hier nicht geöffnet werden, da sie sonst
für die gesamte Existenz der MDB belegt sind.
ejbRemove
Die Methode ejbRemove wird vom Container aufgerufen, wenn der
Pool verkleinert werden soll oder der Server runtergefahren wird.
Entwickler sollten diese Methode zum Schliessen von Ressourcen nutzen.
onMessage
Die Message-Driven-Bean kann zwei Zustände annehmen. Entweder sie
existiert nicht oder sie ist bereit (pooled), Nachrichten zu empfangen. Falls
eine Nachricht eintrifft, wird onMessage ausgeführt.
© 2008 Stephan Metzler
Message Driven Beans
Version 3.0
Seite 126 von total 218 Seiten
J2EE – EJB 2.x
20.3
Übung ee11 : MDB Wetterstation
Wie bereits bei der Betrachtung des Java Message Service (JMS) aufgezeigt gibt es zwei
verschiedene Arten von Mechanismen, nämlich:
Topic:
Topic
Nachrichten haben die Form eines Newsletters oder eines Beitrags in einer Newsgroup. Jeder
Abonnent bekommt die Nachricht zu sehen.
Queue:
Nachrichten haben die Form eines Anrufs in einem Call-Center. Nur der erste freie Bearbeiter,
der den Anruf entgegennimmt, hat damit zu tun.
In beiden Fällen muss man eine Destination einrichten, in dem der Absender die Nachricht
ablegt und aus dem der Empfänger die Nachricht holt.
Bevor man einen Briefkasten benutzen kann, muss man ihn im EJB-Behälter einrichten. Dazu
dient der proprietäre Deployment-Descriptor jbossmq-service.xml:
<server>
<mbean code="org.jboss.mq.server.jmx.Topic"
name="jboss.mq.destination:service=Topic,name=TestTopic">
<depends optional-attribute-name="DestinationManager">
jboss.mq:service=DestinationManager
</depends>
</mbean>
<mbean code="org.jboss.mq.server.jmx.Queue"
name="jboss.mq.destination:service=Queue,name=TestQueue">
<depends optional-attribute-name="DestinationManager">
jboss.mq:service=DestinationManager</depends>
</mbean>
</server>
© 2008 Stephan Metzler
Message Driven Beans
Version 3.0
Seite 127 von total 218 Seiten
J2EE – EJB 2.x
Übung ee11: Wetterstation
Konzipieren Sie einen WetterTopic MDB das eine Wettermessage definiert:
Log: file:///[jboss.home]/server/ee/log/server.log
10:49:46,437 INFO
10:49:46,437 INFO
10:49:46,453 INFO
[STDOUT] WetterMDB: Wetter=Sonnig Wind=West Temp=18.7C
[STDOUT] WetterMDB: Wetter=Sonnig Wind=Ost Temp=3.7C
[STDOUT] WetterMDB: Wetter=Wolkig Wind=Ost Temp=2.9C
Ansatz: WetterBean.java (Fragment)
public void onMessage(javax.jms.Message message) {
try {
MapMessage mapMessage = (MapMessage) message;
System.out.println("WetterMDB: "
+ Wetter=" + mapMessage.getString("Wetterlage")
+ " Wind=" + mapMessage.getString("Windrichtung")
+ " Temp=" + mapMessage.getDouble("Temperatur") + "C");
} catch (Exception ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
Ansatz: WetterClient.java Fragment)
public class WetterClient {
public static void main(String[] args) throws Exception {
Hashtable props = new Hashtable();
props.put("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
props.put("java.naming.provider.url", "jnp://localhost:1099");
props.put("java.naming.factory.url.pkgs",
"org.jboss.naming:org.jnp.interfaces");
props.put("jnp.socket.Factory", "org.jnp.interfaces.TimedSocketFactory");
Context ctx = new InitialContext(props);
TopicConnectionFactory qcf = (TopicConnectionFactory) ctx
.lookup("ConnectionFactory");
Topic topic = (Topic) ctx.lookup("topic/WetterTopic");
TopicConnection connect = qcf.createTopicConnection();
TopicSession session = connect.createTopicSession(false,
TopicSession.AUTO_ACKNOWLEDGE);
connect.start();
TopicPublisher publisher = session.createPublisher(topic);
String[] wetterlagen = { "Sonnig", "Wolkig", "Regen" };
String[] windrichtungen = { "Nord", "West", "Sued", "Ost" };
for (int i = 0; i < 10; i++) {
int rndm = (int) (Math.random() * 300);
String sWetterlage = wetterlagen[rndm % 3];
String sWindrichtung = windrichtungen[rndm % 4];
double dTemperatur = (double) (rndm - 50) / 10;
MapMessage msg = session.createMapMessage();
msg.setString("Wetterlage", sWetterlage);
msg.setString("Windrichtung", sWindrichtung);
msg.setDouble("Temperatur", dTemperatur);
publisher.publish(msg);
System.out.println("-> Wetter=" + sWetterlage + " Wind="
+ sWindrichtung + " Temparatur=" + dTemperatur + "C");
}
session.close();
connect.close();
}
}
© 2008 Stephan Metzler
Message Driven Beans
Version 3.0
Seite 128 von total 218 Seiten
J2EE – EJB 2.x
D
EJB 3.0
21
Alles wird viel einfacher!
Viele Entwickler haben erkannt, dass EJB viel zu kompliziert ist, da man für jede Enterprise
Bean mehrere Artefakte entwickeln muss:
- Ausser der Beanklasse selbst, um die es eigentlich geht, braucht man einige Interfaces
- Man braucht etliche XML-Deskriptordokumente, die nicht leicht zu schreiben sind
- In der Beanklasse werden diverse Callbackmethoden benötigt, die entweder trivial (und
darum nur lästig) oder aber relativ knifflig zu programmieren sind
21.1
EJB 3.0 Spezifikation
Folgende Ziele gelten für die neue Spezifikation:
- Elimination der Deskriptoren
- Kapselung der Abhängigkeiten von der Umgebung und dem JNDI-Zugriff mit Hilfe der in
Java 5 neu eingeführten Annotationen sowie durch Dependency Injection
- Enterprise Beans sollten so weit vereinfacht werden, dass sie eine grössere Ähnlichkeit mit
POJOs bzw. JavaBeans besitzen
- Elimination der speziellen Remote Interfaces (remote und local); hierfür sollten normale
Javainterfaces ausreichen
- Elimination der Home Interfaces
- Elimination von Callbacks. Nicht alle Callbacks waren lästig bzw. überflüssig, man hat solche
übriggelassen (bzw. neue eingeführt), die besonders sinnvoll erscheinen
- Verbesserte Möglichkeiten für das Testen (z. B. Unit Tests) ausserhalb des AS
21.2
EJB 3.0 Features
einfacheres EJB API
EJB 3.0 eliminiert das Home Interfaces und verlangt für die Bean Klassen keine
Implementation der APIs mehr. Session Beans, Message Driven Beans und Entity Beans sind
nun triviale Java Beans, die sich auf die Implementation der Businesslogik konzentrieren. EJB
sind nun einfache Java Klassen (POJO) ohne überladene Interfaces. Entity Beans folgen nun
der normalen objektorientierten (O-O) Entwurfsmuster und profitieren von Vererbung,
Polymorphismus und den standart OO-Konstrukten.
Java Annotationen
EJB 3.0 nützt die seit JDK 5.0 eingeführte Java Annotation als Alternative für das Nennen der
Deploymentparameter. Die EJB 3.0 Specifikation definieren eine Vielzahl von Annotationen, die
Bean Typ, Dependency Injection, Transaction, Security, Callbacks, Object-Relational Mapping,
Relations, etc. beschreiben. Deployment Descriptors dienen weiterhin und können
Annotationen überschreiben. JBoss AS und Hibernate als OR-Mapping unterstützen
Annotationen.
© 2008 Stephan Metzler
Alles wird viel einfacher!
Version 3.0
Seite 129 von total 218 Seiten
J2EE – EJB 2.x
Dependency Injection
Komplexe Szenarien für das Anbinden von EJBs oder Ressourcen verschwinden. Durch die
@Inject, @EJB, und @Resource Annotationen werden diese Abhängigkeiten transparent durch
den Kontainer an die Java Beans angebunden.
Optionale Callbacks
Entwickler müssen nur noch die Callbacks implementieren die sie brauchen. Auch hier erlauben
Annotationen @PostConstruct, @PreDestroy, @PrePersist, oder @PostPersist Java Bean
Methoden mit den Callbacks zu verbinden um entsprechende Event Benachrichtigungen zu
erhalten.
Entity Manager API
Der Entity Manager API ist neu in EJB 3.0 und ermöglicht Java Bean Instanzen sich mit einem
EntityManager zu synchronisieren. JBoss AS wie auch Hibernate unterstützen dieses neue API.
einfachere Persistenz und erweiterte Queries
EJB 3.0 standardisiert das Java Persistenz Modell. Dabei spielt Hibernate eine wichtige Rolle
bei der Definition dieses API. Etliche Java Annotationen wurden definiert um das ObjectRelational Mapping zu definieren. EJB 3.0 erweitert auch die EJB-QL, die nun auch
dynamische Queries, Subqueries, bulk Updates, und bulk Deletes unterstützt.
Einsatz ausserhalb des Kontainers
Die EJB 3.0 Persistenz Spezifikation erlaubt den Gebrauch des neuen Persistenz API auch in
normalen Java Anwendungen. JBoss EJB 3.0 ermöglicht den Gebrauch von Session Beans
und Message Driven Beans auch ausserhalb des JBoss AS in standalone Programme, Junit
Tests, standalone Tomcat, und auch in anderen ASs.
21.3
Meta-Annotationen
ermöglichen:
-
Generierung von Interfaces
Steuerung des Laufzeitverhaltens von EJB-Komponenten
Eliminierung der Deployment Deskriptoren
Demarkation von Injection-Punkten
für:
-
Klassen
Methoden
Parameter
Attribute
© 2008 Stephan Metzler
Alles wird viel einfacher!
Version 3.0
Seite 130 von total 218 Seiten
J2EE – EJB 2.x
Verwendung von MetaMeta-Annotationen im Quellcode:
@Stateless
@Remote (HelloEJB3WorldBean.class)
public class HelloEJB3WorldBean {
String sayHello(){
return "Hello EJB3 World!";
}
// NEU
// NEU
// implements SessionBean fällt weg
generierter Code für das Interface:
public interface HelloEJB3WorldBean {
String sayHello() throws RemoteException;
generierter Deployment Descriptor
…
<session>
<ejb-name>HelloEJB3World</ejb-name>
<home>ejb3.HelloEJB3WorldHome</home>
<remote>ejb3.HelloEJB3World</remote>
<ejb-class>ejb3.HelloEJB3WorldBean</ejb-class>
...
</session>
...
21.4
JNDI Kapselung
- ermöglicht Testen von EJBKomponenten
- J2EE-Applikationsserver als Testumgebung nicht mehr zwingend notwendig
- über setter-Methoden werden einer Komponente zur Laufzeit entsprechende Informationen
zur Verfügung gestellt (injiziert).
- DI : Dependency Injection
- IOC : Inversion of Control (Hollywood-Prinzip: "don't call us – we call you")
Neue Methode <Object lookup(String name)> ist zum EJBContext hinzugefügt, erlaubt direkter
Zugriff für Beans auf den JNDI-Server, ohne extra Code:
Zugriffsvarianten:
- @EJB = Referenz auf EJB-Komponenten
- @Resource = Referenz auf Ressourcen
@Stateless public class MySessionBean {
@Resource(name="myDB") private DataSource customerDB;
public void myMethod (String myString) {
try {
Connection conn = customerDB.getConnection();
...
}
catch (Exception ex) { ...}
}
}
© 2008 Stephan Metzler
Alles wird viel einfacher!
Version 3.0
Seite 131 von total 218 Seiten
J2EE – EJB 2.x
@Session public class MySessionBean {
private DataSource customerDB;
@Resource private void setCustomerDB(DataSource customerDB) {
this.customerDB = customerDB;
}
public void myMethod (String myString) {
...
Connection conn = customerDB.getConnection();
...
}
}
Injection von "Singletons" im Container
@Resource javax.ejb.SessionContext ctx;
@ Resource javax.ejb.MessageDrivenContext ctx;
@ Resource javax.ejb.TimerService timer;
@ Resource javax.ejb.UserTransaction ut;
- @ Resource javax.ejb.EntityManager manager;
-
21.5
Configuration by Exception
Spezifiziertes Default-Verhalten von EJB-Komponenten mit dem Ziel der Reduzierung der
Entwicklungszeit
Beispiel:
- EJB-Komponenten (Session Beans) sind per default "lokal" zugreifbar und erlauben eine
explizite Steuerung durch Verwendung der Annotationen
- @Local und @Remote für das Interface bzw. die Bean-Klasse
- auf Attribute von Entity Beans wird per default über get/set-Methoden zugegriffen und
erlaubt eine explizite Steuerung durch Verwendung der Werte der Annotation
- @Entity(access=PROPERTY) und @Entity(access=FIELD)
21.6
Callback-Annotationen für Lifecycle Events
- vordefinierte Callbacks
- gekennzeichnet durch Annotationen (z.B. @PostConstruct, @PreDestroy)
- Session Beans, Message-Driven Beans, Entity Beans
21.7
Interzeptor-Methoden
- Abfangen von Geschäftsmethodenaufrufen
- ermöglicht die Ausführung von beliebigem Code
- z.B. technische Prüfungen, Security
- Session Beans, Message-Driven Beans
© 2008 Stephan Metzler
Alles wird viel einfacher!
Version 3.0
Seite 132 von total 218 Seiten
J2EE – EJB 2.x
21.8
Stateless Session Bean
Das Home Interface (und deren create Methode) entfällt, dadurch ist der Zugriff auf die EJB
Komponente aus Sicht des Clients vereinfacht:
Version 2.1
public static void main(...) throws Exception {
InitialContext ctx = new InitialContext();
Object o = ctx.lookup("MyEJB3");
MyEJB3Home home = (MyEJB3Home) PortableRemoteObject.narrow(o, MyEJB3Home.class);
MyEJB3 myEJB3 = home.create();
}
Version 3.0
public static void main(...) throws Exception {
InitialContext ctx = new InitialContext();
MyEJB3 myEJB3 = (MyEJB3) ctx.lookup("MyEJB3");
... // do something
}
- Business (Remote) Interface muss weiterhin vorhanden sein:
- als einfaches Java-Interface (POJI), ohne extens (EJBObject / EJBLocalObject)
- somit auch keine RemoteExceptions mehr in den Methoden
- Business (Remote) Interface kann auch generiert werden
- Bean-Klasse muss nicht mehr das Callback-Interface javax.ejb.SessionBean implementieren
- ejbRemove() gibt es nicht mehr, ist für eine Stateless Session Bean sowieso eine völlig
überflüssige Methode
- @Stateless annotiert Bean-Klasse als Stateless Session Bean
Component Interface
Interface (Remote(Remote- oder Business Interface):
public Interface EJB3 {
public void sayHello();
}
Bean Klasse:
@Stateless public class EJB3Bean implements EJB3 {
public void sayHello() {
return "Hello EJB3 World!";
}
© 2008 Stephan Metzler
Alles wird viel einfacher!
Version 3.0
Seite 133 von total 218 Seiten
J2EE – EJB 2.x
21.9
Statefull Session Bean
Das Home Interface (und deren create Methode) entfällt, dadurch ist der Zugriff auf die EJB
Komponente aus Sicht des Clients vereinfacht:
- Initialisierung der Bean erfolgt durch die Geschäftsmethode, (ev. über Annotation)
- create Methode kann im Component Interface vorhanden sein
Business (Remote) Interface muss weiterhin vorhanden sein:
- als einfaches Java-Interface (POJI), ohne extens (EJBObject / EJBLocalObject)
- somit auch keine RemoteExceptions mehr in den Methoden
- Business (Remote) Interface kann auch generiert werden
- Bean-Klasse muss nicht mehr das Callback-Interface javax.ejb.SessionBean implementieren
- die remove() Methode (annotiert durch @Remove) kann eine beliebige Methode der Bean
Klasse sein
- @Stateful annotiert Bean-Klasse als Stateful Session Bean
Component Interface
Interface (Remote(Remote- oder Business Interface):
public Interface AutomaticTellerEJB3 {
public void depositMoney(float amount);
public void withdrawMoney(float amount);
public float getAccountBalance();
@Remove public void closeAccount();
}
Bean Klasse:
@Stateful public class AutomaticTellerEJB3Bean implements AutomaticTellerEJB3
{
public void depositMoney(float amount) {
accountBalance += amount);
}
public void withdrawMoney(float amount) {
accountBalance -= amount);
}
public float getAccountBalance() {
return accountBalance;
}
@Remove public void closeAccount() {
... // do something do cleanup
}
}
© 2008 Stephan Metzler
Alles wird viel einfacher!
Version 3.0
Seite 134 von total 218 Seiten
J2EE – EJB 2.x
21.10
Message Driven Bean
Ein MDB muss entweder mit @MessageDriven Annotation gekennzeichnet werden oder das
Callback-Interface javax.ejb.MessageDrivenBean implementieren.
21.11
Entity Bean
Der Schwerpunkt bildet die CMP mit einer Weiterentwicklung der Persistenzfunktionalität, eine
einfachere CMP/BMP Nutzung und einen höheren Leistungsumfang.
Ziel: Simplifizierung von Entity Beans durch POJOs
konkrete (keine abstrakte) Klasse
@Entity annotiert Bean-Klasse als Entity Bean
leichtgewichtiges Domänen-Objekt
Entity Beans sind nicht mehr direkt entfernt zugreifbar!
-
Zugriff erfolgt nur noch über neu eingeführten javax.ejb.EntityManager zum:
- Erzeugen und Löschen von persistenten Entity-Instanzen
- Finden von Entitäten über Primärschlüssel
- Abfragen über Entitäten
Beispiel
@Stateless public class Product {
EntityManager em;
@Inject public void setEntityManager(EntityManager em) {
this.em = em;
}
public int createProduct(String name, double price) {
Product product = new Product(name, price);
em.create(order);
return order.getId();
}
public Product find(int id) {
return em.find(Product.class, id);
}
public void enterProduct(int custID, Product newProduct) {
Customer cust = (Customer)em.find("Customer", custID);
cust.getProducts().add(newProduct);
newProduct.setCustomer(cust);
}
}
© 2008 Stephan Metzler
Alles wird viel einfacher!
Version 3.0
Seite 135 von total 218 Seiten
J2EE – EJB 2.x
Entity Beans beherrschen nun Vererbung und Polymorphie:
-
Polymorphe EJB QL-Abfragen sind nun zulässig
kein Home Interface mehr nötig
kein Business Interface mehr nötig (technisch gesehen)
JavaBean-Stil
Attribute sind nur per getter/setter-Methoden zugreifbar
Eliminieren der komplexen, aufwendigen und fehleranfälligen Deployment Deskriptoren
- Beziehungen etc. werden in den Klassen per Annotationen deklariert
- kein DTO (Data Transfere Object) für Transport von Entity Bean-Daten mehr notwendig
- Entity Bean selbst wird transferiert (detached object)
- nach Änderungen am "detached object" können diese mit dem persistenten Zustand
zusammengebracht werden (reattached object)
Beispiel: Bean Klasse
@Entity
@Table(name = "PURCHASE_ORDER")
public class Order implements java.io.Serializable {
private int id;
private double total;
private Collection<LineItem> lineItems;
@Id(generate = GeneratorType.AUTO) public int getId() { return id; }
public void setId(int id) { this.id = id; }
public double getTotal() { return total; }
public void setTotal(double total) { this.total = total;}
public void addPurchase(String product, int quantity, double price) {
if (lineItems == null) lineItems = new ArrayList<LineItem>();
LineItem item = new LineItem();
item.setOrder(this);
...
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "order_id")
public Collection<LineItem> getLineItems() {
return lineItems;
}
public void setLineItems(Collection<LineItem> lineItems) {
this.lineItems = lineItems;
}
}
© 2008 Stephan Metzler
Alles wird viel einfacher!
Version 3.0
Seite 136 von total 218 Seiten
J2EE – EJB 2.x
21.12
EJB QL
Weiterentwicklung der EJB QL sind:
Sub Queries
SELECT goodCustomer
FROM Customer goodCustomer
WHERE goodCustomer.balance < (
SELECT avg(c.balance) FROM Customer c
)
GroupBy / Having
SELECT c.status, avg(c.filledOrderCount), count(c)
FROM Customer c
GROUP BY c.status
HAVING s.status IN (1, 2)
Explizite InnerInner-Joins und OuterOuter-Joins / Projection
SELECT c.id, c.status
FROM Customer c JOIN c.orders o
WHERE o.count > 100
Liefert ein Array von java.lang.Object
SELECT new CustomerDetails(c.id, c.status, o.count)
FROM Customer c JOIN c.orders o
WHERE o.count > 100
Liefert ein Array von CustomerDetails
Bulk updates, Bulk deletes
DELETE FROM Customer c
WHERE o.status = "inactive" AND c.orders IS EMPTY
Auch werden native SQL-Abfragen unterstützt und weiter zusätzliche Funktionen in der
WHERE-Klausel sind zulässig:
-
UPPER
LOWER
TRIM
POSITION
CHARACTER_LENGTH
CHAR_LENGTH
BIT_LENGTH
CURRENT_TIME
CURRENT_DATE
CURRENT_TIMESTAMP
© 2008 Stephan Metzler
Alles wird viel einfacher!
Version 3.0
Seite 137 von total 218 Seiten
J2EE – EJB 2.x
Dynamische Abfragen, NamedNamed-Queries
// Erzeugen einer NamedQuery
...
@NamedQuery(
name="findAllCustomersWithName",
queryString="SELECT c FROM Customer c WHERE c.name LIKE :custName"
)
...
// Nutzen einer NamedQuery
...
@Inject public EntityManager em;
customers = em.createNamedQuery("findAllCustomersWithName")
.setParameter("custName", "Smith")
.listResults();
...
Polymorphe Abfragen
Liefert als Ergebnis nicht nur den spezifizierten Typ, sondern auch alle dazugehörigen
Subtypen.
21.13
Zusammenfassung
- Statt komplexer Komponenten einfache POJOs / POJIs
- Verkürzung der Entwicklungszeit
- Wegfall von überflüssigen Interfaces (Home Interface, Remote Interface und der TypenCallback Interfaces)
- Bessere und einfachere Modellierbarkeit
- Einfache Java-Klassen mit allen Vorteilen von Container-Komponenten
- Statt Deployment Deskriptoren Meta-Annotation im Java-Code
- Nur noch eine Codebasis
- Erlaubt nun Prüfungen zur Kompilierungszeit
- Inversion of Control / Dependency Injection
- Configuration by Exception
- Vereinfachung der Entwicklung (Reduzierung Code und Entwicklungszeit)
- Mit EJB 3.0 wird die Komponententechnologie erheblich einfacher und gleichzeitig
mächtiger.
- EJB 3.0 wird die Entwicklung von komponenten-basierten Systemen beschleunigen
- Konkurrenz .NET
- Das leidige Thema der konkurrierenden Persistenzstrategien innerhalb von J2EE/J2SE
wird mit EJB 3.0 endlich behoben
© 2008 Stephan Metzler
Alles wird viel einfacher!
Version 3.0
Seite 138 von total 218 Seiten
J2EE – EJB 2.x
E
Aufsetzen und Konfiguration der Entwicklungsumgebung
Entwicklungsumgebung
22
Entwicklungstools für J2EE
Der Markt bietet neben kommerziellen AS Produkten, die mehr oder weniger hohe
Lizenzkosten beinhalten auch einige Open-Source Projekte an. Dieses Skript und die
aufgeführten Beispiele fokussieren bewusst auf OS Tools um das Testen der Lerninhalte so
einfach wie möglich zu gestalten.
Die folgenden Installationsanleitungen und die Beschreibung der Entwicklungs-Tools basieren
auf den im Skript aufgeführten Beispielsübungen und deren Lösungen.
Die Beispiele in diesem Skript basieren auf den nachfolgend installierten Versionen. Neue
Releases sind meist "down-kompatible", eventuelle Anpassungen an den Source Code der
Beispiele müssen gemäss den entsprechenden Handbüchern gemacht werden. Die Beispiele
sind auch auf einer EJB 3 konformen Umgebung einsetzbar, entsprechen jedoch nicht der EJB
3 Spezification.
23
Java J2SE / J2EE Installation
Neben einem JDK (z.B. JDK1.5) muss für die Entwicklung von J2EE
Applikationen die J2EE Umgebung von Sun™ installiert werden (z.B.
J2EESDK1.4). Beide Dateien, sowie etliche Dokumentationen können
unter http://java.sun.com bezogen und lokal installiert werden.
J2SE installieren:
ausführen:
von:
jdk-1_5….exe
[CD]\install\j2se
oder von http://java.sun.com
J2EE installieren:
ausführen: j2eesdk-1_4….exe
[CD]\install\j2ee
von:
oder von http://java.sun.com
© 2008 Stephan Metzler
Entwicklungstools für J2EE
Version 3.0
Seite 139 von total 218 Seiten
J2EE – EJB 2.x
24
Eclipse als Entwicklungsumgebung
Eclipse.org ist die Plattform für mehr Hintergrundinformationen betreffend der Eclipse
Entwicklungsumgebung:
- Basiert auf Initiative von IBM
- Mitarbeit von Borland, webgain, GNX, serena, Rational, redhat, SuSe, TogetherSoft, ...
- Entwickelt von mehr als 1200 Software Entwicklern von mehr als 150 führenden Software
Herstellern aus mehr als 60 verschiedenen Ländern
( einige ) Plattform Komponenten:
-
24.1
Ant ( integration of Ant scripting )
Compare ( universal compare facility )
Debug ( generic execution and debug framework )
Help ( platform help system )
Installation
Die aktuelle Version von Eclipse kann als ZIP Datei von http://www.eclipse.org oder direkt
unter http://mirror.switch.ch/mirror/eclipse/downloads/ bezogen werden und folgendermassen
installiert werden:
zu einem lokalen Verzeichnis dekomprimieren (Verzeichnisstruktur sollte keine Leerzeichen
enthalten, wie z.B. "Eigene Dateien"):
entpacken:
eclipse-SDK-3.2-win32.zip
von:
[CD]\install\eclipse
nach:
...\ee
bevorzugte PlugPlug-Ins installieren, z.B: Quantum DB Explorer:
entpacken:
Quantum_2.4.5_for_Eclipse_3.zip
von:
[CD]\install\eclipse plugins
nach:
...\ee\eclipse\
bevorzugte PlugPlug-Ins installieren, z.B: XMLBuddy:
XMLBuddy:
entpacken:
xmlbuddy_2.0.72.zip
von:
[CD]\install\eclipse plugins
nach:
...\ee\eclipse\plugins
weitere Eclipse PlugPlug-Ins unter:
http://www.eclipseplugincentral.com
24.2
Eclipse starten
Im Insallationsverzeichnis auf das entsprechende Icon klicken.
© 2008 Stephan Metzler
Eclipse als Entwicklungsumgebung
Version 3.0
Seite 140 von total 218 Seiten
J2EE – EJB 2.x
25
JBoss AS
JBoss war ursprünglich als EJB Kontainer konzipiert,
wurde aber zu einem kompletten Applikations Server
für J2EE Applikationen weiterentwickelt.
Entstanden 1999 - JBoss ist das Produkt einer
OpenSource Developer Community die das erklärte
Ziel hatte, den besten J2EE-compliant
Applikationsserver auf dem Markt zu entwickeln
- 1000 Developer weltweit
- JBoss ist heute wahrscheinlich der verbreitetste EJB Server
- 4000 Downloads des EJB Servers pro Tag (mehr als manche kommerzielle Hersteller
insgesamt haben)
- Die Distribution erfolgt unter LGPL License, d.h. JBoss kostet keine Lizenzgebühren.
- JBoss hat sich als eine vitale Alternative zu überpreisten EJB Servern im kommerziellen
Umfeld entwickelt.
25.1
Installation
Die aktuelle Version von JBoss kann als ZIP Datei von http://www.jboss.org bezogen werden
und folgendermassen installiert werden:
zum lokalen Verzeichnis C:\ee dekomprimieren (Verzeichnisstruktur sollte keine Leerzeichen
enthalten, wie z.B. "Eigene Dateien")
entpacken: jboss-4.0.4.GA.zip
von:
[CD]\install\jboss
oder http://www.jboss.org
nach:
...\ee\
JAVA_HOME Umgebungsvariable als Benutzer- oder Systemvariablen entsprechend der
installierten JDK Version definieren/ergänzen:
25.2
JBoss AS starten / stoppen
- das run.bat Skript in \bin Verzeichnis der JBoss Installation ausführen
- mit http://localhost:8080 den JBoss Server testen.
- CTRL+C, shutdown.bat oder über die Management Konsole
http://localhost:8080/jmx-console
kann der JBoss Applikations Server gestoppt werden.
© 2008 Stephan Metzler
JBoss AS
Version 3.0
Seite 141 von total 218 Seiten
J2EE – EJB 2.x
25.3
JBoss Verzeichnisstruktur
Das Installationsverzeichnis enthält fünf Ordner:
- bin
- client
- docs
- lib
Skript Files
JAR Dateien für Client Applikationen,
jbossall-client.jar enthält sämtliche Lybraries
enthält XML DTD für JBoss Referenzen sowie
example J2EE Connector Architecture Dateien für das
Anbinden von externen Ressourcen (z.B. MySQL, etc.)
JBoss microkernel JAR Lybraries
- server
- all
die verschiedenen server Konfigurationen
startet alle zur Verfüfung stehenden Dienste
wie: RMI-IIOP, Clustering und WEB-Service
- default started in der standart Konfiguration
- ee
(erstellter) benutzerspezifischer Server für
die EnterpriseEducation Beispiele: ee4, ...
(als Kopie des default Verzeichnis)
- minimal minimum Requirement für den JBoss Server
mit: JNDI und URL Deployment Scanner
ohne: WEB- oder EJB Container, ohne JMS
Der entsprechende Server ist, wenn gestartet (z.B. run –c default),
auch zugleich das entsprechende Verzeichnis, indem alle Deployments stattfinden und alle
LOGs geschrieben werden. Die Verzeichnisstruktur beschreibt:
25.4
- conf
enthält die jboss-service.xml Datei, die den core Service spezifiziert
- data
- deploy
- lib
- log
- tmp
Verzeichnis für die Hypersonic Datenbank
HOT-Deployment Verzeichnis für JAR, WAR und EAR Dateien
JAR Dateien für die Server Konfiguration und Ressourcen
z.B. "fremde" JDBC Drivers, etc.
Verzeichnis für die Logging Informationen
Temporäres Verzeichnis für Deployment, etc.
- work
JSP-Kompilationsverzeichnis für Tomcat
Benutzerspezifischer Server einrichten
Als Default wird der "default" Sever gestartet. Alle serverspezifischen Informationen sind im
entsprechenden Verzeichnis. Mit den Beispielsapplikationen benutzen wir mySQL als
Datenbank. Um einen entsprechenden benutzerspezifischen Server "ee" für die
EnterpriseEducation Beispiele zu definieren sind die folgenden Schritte massgebend:
[JBoss_Home]\server\default Verzeichnis kopieren zu "ee"
kopieren:
Verzeichnis
von:
[JBoss_Home]\server\default
nach:
[JBoss_Home]\server\ee
© 2008 Stephan Metzler
JBoss AS
Version 3.0
Seite 142 von total 218 Seiten
J2EE – EJB 2.x
Der JDBC Treiber wird als "MySQL Connector" auf der offiziellen Website von MySQL
angeboten, wobei die im Root des heruntergeladenen TAR- oder ZIP Verzeichnis befindliche
JAR-Datei von Bedeutung ist. Diese im JAR-Format befindliche Java-Bibliothek muss in den
/lib Ordner der JBoss Installation kopiert werden - damit erhält der Application Server den
Zugriff darauf, und die Voraussetzung, mit dem MySQL Server kommunizieren zu können.
JDBC Driver mysql-connector-java-3.0.15-ga-bin.jar oder aktuellen Connector/J von
http://www.mysql.com ins Verzeichnis [JBoss_Home]\server\ee\lib kopieren:
kopieren:
mysql-connector-java-5.0.3-bin.jar
von:
[CD]\install\mysql
http://www.mysql.com
nach:
[JBoss_Home]\server\ee\lib
Die logische Repräsentation der MySQL Datenbank innerhalb der J2EE Programme wird nicht,
wie vermutet, durch ein direktes Verwenden des JDBC-Treibers geschaffen, sondern über eine
abstrakte Datenbankschnittstelle, DataSource genannt. Durch eine neu anzulegende
Konfigurationsdatei im /deploy Ordner der JBoss Installation können DataSources zentral
durch den Namens- und Verzeichnisdienst verwaltet werden.
In /docs/examples/jca/mysql-ds.xml befindet sich eine Vorlage, die den
Verhältnissen entsprechend angepasst werden muss.
mySQL DataSource Konfigurationsdatei mysql-ds.xml:
kopieren:
mysql-ds.xml
nach:
[JBoss_Home]\server\ee\deploy
anpassen:
benutzerspezifisch konfigurieren
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>mySSqlDS</jndi-name>
<connection-url>
jdbc:mysql://localhost:3306/ee
</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>root</user-name>
<password></password>
</local-tx-datasource>
</datasources>
25.5
Benutzerspezifischer JBoss-Server starten
Um den neu erstellten Server "ee" zu starten muss vorgängig mySQL installiert, sowie die
entsprechende Datenbank "ee" mit den entsprechenden Benutzerrechten erstellt sein.
- cd\ C:\ee\ jboss-4.0.4.GA\bin\
- run -c ee
Das entsprechende Script um den Server zu stoppen ist: shutdown -S
© 2008 Stephan Metzler
JBoss AS
Version 3.0
Seite 143 von total 218 Seiten
J2EE – EJB 2.x
26
mySQL Datenbankserver installieren und Datenbank erstellen
mySQL ist eine der verbreitetsten Open Source Datenbanken und wird
von vielen Organisationen kommerziell benutzt.
26.1
Installation
entpacken:
mysql-noinstall-5.0.24-win32.zip
von:
[CD]\install\mysql
oder http://www.mysql.com
nach:
c:\ (entziptes Verzeichnis
Verzeichnis umbenennen zu mysql!)
mySQL kann bei der Installation und beim Starten des
DBMS Dienstes Probleme verursachen wenn diese
mySQL Distribution nicht im c:\ (root) Verzeichnis liegt:
26.2
mySQL-Tools
Zur windowbasierten Administration von mySQL DBMS
können verschiedene Tools installiert werden, z.B:
entpacken:
mysql-gui-tools-noinstall-5.0-r2-win32
von:
[CD]\install\mysql
oder http://www.mysql.com
nach:
26.3
c:\
EnterpriseEducation DB anlegen
Mit Administrationstool oder per Konsole den Datenbankserver starten und die Datenbank "ee"
erstellen:
-
c:\>set path %PATH%;c:\mysql\bin
c:\>mysqld-nt.exe --install
c:\>net start mysql
c:\>mysqladmin -u root create ee
c:\>mysql –u root –D e
mysql> GRANT ALL PRIVILEGES
ON ee.*
TO 'Ihr_Name'@localhost
IDENTIFIED BY 'Ihr_Password'
© 2008 Stephan Metzler
mySQL Datenbankserver installieren und Datenbank erstellen
Version 3.0
Seite 144 von total 218 Seiten
J2EE – EJB 2.x
27
EJB Applikation mit Eclipse / JBoss
Für das Deployen einer mit der Eclipse Plattform erstellten EJB Applikation steht eine build.xml
Datei zur Verfügung. Diese muss im root Verzeichnis des Eclipse Projectes liegen und
implementiert die folgenden Targets:
- deploy
Deployment von Sample Applikationen
- run-client-application
Ausführen von Client Applikation
- run-sql-script
Ausführen von SQL Statements
Apache Ant ist ein Java basiertes Build Tool und ist
integrierter Bestandteil der Eclipse Entwicklungsumgebung. Information und Beispielanwendungen sind
unter http://ant.apache.org erhältlich.
Apache Ant build.xml Script für die Enterprise Education Beispiele:
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Enterprise Education:
deploy
: Deployment von Sample Applikationen
run-client-application : Ausführen von Client Applikation
run-sql-script
: Ausführen von SQL Statements
Author : s.metzler
Datum
: 2006-08-20
Version : 3.0
-->
<project name="EnterpriseEducation Packaging and Deployment Script"
default="deploy" basedir=".">
<!-- Property Datei: enthält Applikationsspezifische Parameter -->
<property file="${basedir}/properties/enterpriseeducation.properties" />
<!-- Resources dir: enthält jndi.properties Datei für den Client -->
<property name="resources.dir" value="${basedir}/resources" />
<!-- Client Classpath: Classpath Parameter für den Client -->
<path id="client.classpath">
<pathelement location="${resources.dir}" />
<fileset dir="${jboss.home}/client">
<include name="**/*.jar" />
</fileset>
</path>
<!-- Build Classpath: Classpath Parameter für den Build Prozess -->
<path id="build.classpath">
<path refid="client.classpath" />
<pathelement location="${build.dir}" />
<pathelement location="${servlet.jar}" />
</path>
<!-- SQL Classpath: Classpath Parameter für die Datenbankanbindung -->
<path id="sql.classpath">
<pathelement location="${mysql.driver.jar}" />
</path>
© 2008 Stephan Metzler
EJB Applikation mit Eclipse / JBoss
Version 3.0
Seite 145 von total 218 Seiten
J2EE – EJB 2.x
<!-- depoly target: Deployment von Sample Applikationen -->
<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<target name="deploy">
<mkdir dir="${build.dir}" />
<!-- Bean Klassen kompilieren -->
<javac destdir="${build.dir}" classpathref="build.classpath">
<src path="${src.dir}/${ejb.dir}" />
</javac>
<!-- Client Klassen kompilieren -->
<javac destdir="${build.dir}" classpathref="build.classpath">
<src path="${src.dir}/${client.dir}" />
</javac>
<!-- Bean Archivedatei löschen -->
<delete file="jar/${app.name}.jar" />
<!-- Bean Deployment Descriptor kopieren -->
<copy file="dd/ejb-jar.xml" tofile="${build.dir}/ejb-jar.xml"
overwrite="true" />
<copy file="dd/jboss.xml" tofile="${build.dir}/jboss.xml"
overwrite="true" />
<!-- Bean Archivedatei bilden -->
<jar jarfile="jar/${app.name}.jar">
<metainf dir="${build.dir}" includes="ejb-jar.xml, jboss.xml" />
<fileset dir="${build.dir}">
<include name="${ejb.dir}/**" />
</fileset>
</jar>
<!-- Bean Archivedatei deployen -->
<copy file="jar/${app.name}.jar" todir="${jboss.deploy.dir}" />
<!-- Client Archivedatei löschen -->
<delete file="jar/${app.name}AppClient.jar" />
<!-- Client Deployment Descriptor(en) kopieren -->
<copy file="dd/application-client.xml"
tofile="${build.dir}/application-client.xml" overwrite="true" />
<copy file="dd/jboss-client.xml"
tofile="${build.dir}/jboss-client.xml" overwrite="true" />
<!-- Client Archivedatei bilden -->
<jar jarfile="jar/${app.name}AppClient.jar">
<metainf dir="${build.dir}"
includes="application-client.xml, jboss-client.xml" />
<fileset dir="${build.dir}">
<include name="${resources.dir}/jndi.properties" />
<include name="${client.dir}/**" />
<include name="${ejb.dir}/${app.name}Home.class" />
<include name="${ejb.dir}/${app.name}.class" />
</fileset>
<fileset dir="${resources.dir}">
<include name="jndi.properties" />
</fileset>
</jar>
<!-- Tempöräres Build Verzeichnis löschen -->
<delete dir="${build.dir}" />
</target>
<!-- run-client-application target: Ausführen von Client Applikation -->
<!-- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<target name="run-client-application" depends="deploy">
<java classname="client.${app.name}Client" fork="yes">
<classpath>
<pathelement path="jar/${app.name}AppClient.jar" />
<path refid="client.classpath" />
<pathelement path="${java.class.path}" />
</classpath>
</java>
</target>
© 2008 Stephan Metzler
EJB Applikation mit Eclipse / JBoss
Version 3.0
Seite 146 von total 218 Seiten
J2EE – EJB 2.x
<!-- run-sql-script target: Ausführen von SQL Statements -->
<!-- +++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<target name="run-sql-script">
<sql classpathref="sql.classpath"
driver="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost/${database}"
userid="${user}"
password="${password}"
src="sql/${sql.data}" />
</target>
</project>
Die Parameter des InitialContext PROVIDER_URL, NITIAL_CONTEXT_FACTORY, und
URL_PKG_PREFIXES sind in einer Property Datei definiert:
resources/jndi.properties
resources/jndi.properties
# Definiert die JBoss Server Context Properties
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=jnp://localhost:1099
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
Die zur Ausführung dieses Skripts notwendigen applikationsspezifischen Parameter sind in einer
PropertyDatei untergebracht:
properties/enterpriseeducation.properties
# Enterprise Education Properties
# zum Deployen und Ausführen der JBoss Beispiele
# Pfad des Projekt Verzeichnis
ee.home=D:/ee
# JBoss-spezifische Einstellungen
# ------------------------------jboss.home=${ee.home}/jboss-4.0.4.GA
# Pfad des JBoss Server
jboss.server=${jboss.home}/server/ee
# Pfad des deploy Ordner
jboss.deploy.dir=${jboss.server}/deploy
# den Pfad des Servlet Archives angeben
servlet.jar=${jboss.server}/lib/javax.servlet.jar
# Applikations-spezifische Einstellungen
# ------------------------------------# Name der Applikation
app.name=MoneyExchange
# Source Verzeichnis
src.dir=${basedir}/src
# Beans Verzeichnis
ejb.dir=beans
# Client Verzeichnis
client.dir=client
# Build Verzeichnis (temporäres Verzeichnis)
© 2008 Stephan Metzler
EJB Applikation mit Eclipse / JBoss
Version 3.0
Seite 147 von total 218 Seiten
J2EE – EJB 2.x
build.dir=${basedir}/build
# Name der ClientApplikation
j2ee.clientName=MonexExchangeClient
# Datenbank-spezifische Einstellungen
# ------------------------------------# Connector/J Archive
mysql.driver.jar=${ee.home}/lib/mysql-connector-java-5.0.3-bin.jar
# SQL Skript Datei
sql.data=create_rates_table.sql
# Benutzereinstellungen für DBMS
user=root
password=
database=ee
Das Apache Ant Skript verlangt zusammen mit den applikationsspezifischen Parametern nach
einer entsprechenden Struktur innerhalb der Eclipse Entwicklungsumgebung. Diese ist wie folgt
abgebildet (Beispiel MoneyExchange):
Project: ee4 (EnterpriseEducation Beispiel
Beispiel 4)
- ee4
Projektordner
- bin
default output folder
- beans
wird generiert
- client
wird genertiert
- dd
Deployment Descriptor Dateien
- jar
generierte JAR Dateien
- properties
Property Dateien
- resources
Resource Dateien
- sql
SQL Skript Dateien
- src
source folder on build path
- beans
Home Interface Class
Remote Interface Class
Bean Implementation Class
- client
Client Applikations Class
- build.xml
Apache Ant Datei liegt im
Project-Verzeichnis!
Classpath Einstellungen:
© 2008 Stephan Metzler
EJB Applikation mit Eclipse / JBoss
Version 3.0
Seite 148 von total 218 Seiten
J2EE – EJB 2.x
28
Tomcat
Tomcat Version 5 implementiert die Servlet 2.4- und JavaServer Pages 2.0
Specifikation, definiert durch den Java Community Process
(http://www.jcp.org/) und beinhaltet viele zusätzliche Funktionen die eine
Entwicklung und das Deployen von Web Applikationen unterstützen.
28.1
Server Architektur
Web Applokationen liegen im Verzeichnis [Tomcat_Home]\
[Tomcat_Home]\webapps\
webapps\. Per Default soll ein
\WEBWEB-INF\
INF\ Ordner die Deploydateien enthalten.
Der entsprechende Eintrag in der WebserverWebserver-Konfigurationsdatei
Konfigurationsdatei
[TOMCAT_HOME]\\conf\
[TOMCAT_HOME]
conf\server.xml weist auf das entsprechende Verzeichnis der WEBApplikation.
Ressourcen, die direkt zum Client geschickt werden oder vom Client aus ansprechbar sind,
wie z.B. .jsp-, .html- und .gif-Dateien sowie clientseitig nutzbare .class-Applets,
befinden sich nicht unterhalb des Verzeichnisses WEB-INF, sondern parallel dazu.
- Unter /WEB_INF/classes befinden sich Servlets,
JavaBeans und andere Server-seitige .class-Dateien.
- Unter /WEB_INF/lib befinden sich .jar-Archive.
- /WEB_INF/web.xml ist die Konfigurationsdatei der Web
Application.
- In die Webserver-Konfigurationsdatei
[TOMCAT_HOME]\conf\server.xml muss die Web
Application mit einem Context-Element angemeldet werden.
Beschreibung des Context Tags
Element Attribute
Beschreibung
Default
Context path
URL Fragment um den Kontext zu identifizieren.
none, must
be
specified
Path zum Projektverzeichnis.
none, must
be
specified
docBase
© 2008 Stephan Metzler
Tomcat
reloadable Erlaubt das "Reloading" der Servlets für diesen Kontext.
true
debug
0
Debug Level für das Logging
Version 3.0
Seite 149 von total 218 Seiten
J2EE – EJB 2.x
Beispiel eines Eintrags in der Webserver Konfigurationsdatei:
Konfigurationsdatei:
<Context path="/examples"
docBase="webapps/examples"
debug="0"
reloadable="true" >
oder:
<Context path="/ee4"
docBase="C:\ee\workspace\ee4"
workDir="C:\ee\workspace\ee4\work" />
debug="0"
reloadable="true" >
28.2
Installation
entpacken:
jakarta-tomcat-5.0.28.zip
von:
[CD]\install\tomcat
oder von http://jakarta.apache.org/site/binindex.cgi
nach:
...\ee\
Tomcat PlugPlug-In installieren
entpacken:
tomcatPluginV31.zip
von:
[CD]\install\eclipse plugins
nach:
...\ee\eclipse\plugins
Um einen Portkonflikt mit dem JBoss AS zu vermeiden muss der Port in der Datei
..\conf\server.xml umkonfiguriert werden.
- <Connector port="8090" …
- Tomcat starten mit : http://localhost:8090
© 2008 Stephan Metzler
Tomcat
Version 3.0
Seite 150 von total 218 Seiten
J2EE – EJB 2.x
F
Lösungen zu den Übungen
29
Lösungen zu Übungen
Übungen aus dem Skript
Die abgedruckten Lösungen sind auch auf beiligender CD enthalten.
29.1
Lösung zu Übung ee1 : Stateless Session Bean – Zinsberechnung
Formel zur Zinsberechnung:
amount x (
years
rate + 100
)
100
Home Interface: ZinsHome.java
package ejb;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface ZinsHome extends EJBHome {
Zins create() throws CreateException, RemoteException;
}
Remote Interface: Zins.java
package ejb;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Zins extends EJBObject {
double calculate(double amount, double rate, int years)
throws RemoteException;
}
Bean Class: ZinsBean.java
package ejb;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
public class ZinsBean implements SessionBean {
public double calculate(double amount, double rate, int years) {
return amount * Math.pow((rate / 100.0) + 1.0, (double) years);
}
public
public
public
public
public
void
void
void
void
void
ejbCreate() {}
ejbRemove() {}
ejbActivate() {}
ejbPassivate() {}
setSessionContext(SessionContext sc) {
}
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 151 von total 218 Seiten
J2EE – EJB 2.x
Client Application: ZinsClient.java
package client;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import ejb.Zins;
import ejb.ZinsHome;
public class ZinsClient {
public static void main(String[] args) {
double amount = 100.0;
double rate = 10.0;
int years = 5;
try {
Context initial = new InitialContext();
Object objref = initial.lookup("ZinsBean");
ZinsHome home = (ZinsHome) PortableRemoteObject.narrow(objref,
ZinsHome.class);
Zins zins = home.create();
System.out.println("Betrag:\t" + amount + "\nZins:\t" + rate
+ "\nJahre:\t" + years);
System.out.println("Result:\t"
+ zins.calculate(amount, rate, years));
} catch (Exception ex) {
System.err.println("unexpected exception!");
ex.printStackTrace();
}
}
}
Output:
Betrag:
Zins:
Jahre:
Result:
100.0
10.0
5
161.05100000000004
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 152 von total 218 Seiten
J2EE – EJB 2.x
29.2
Lösung zu Übung ee2 : Stateful Session Bean – Kontoführung
Home Interface: TellerHome.java
package ejb;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface TellerHome extends EJBHome {
Teller create() throws CreateException, RemoteException;
}
Remote Interface: Teller.java
package ejb;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Teller extends EJBObject {
public void depositMoney(float inAmount) throws RemoteException;
public void withdrawMoney(float inAmount) throws RemoteException;
public float getAccountBalance() throws RemoteException;
public float getRate() throws RemoteException;
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 153 von total 218 Seiten
J2EE – EJB 2.x
Bean Class: TellerBean.java
package ejb;
import
import
import
import
javax.ejb.SessionBean;
javax.ejb.SessionContext;
javax.naming.Context;
javax.naming.InitialContext;
public class TellerBean implements SessionBean {
private float accountBalance;
private float rate = 0.5f;
private SessionContext context;
public float getRate() {
return rate;
}
public void depositMoney(float inAmount) {
accountBalance += inAmount;
System.out.println("Deposition of " + inAmount + ".- CHF!");
}
public void withdrawMoney(float inAmount) {
accountBalance -= inAmount;
System.out.println("Withdrawel of " + inAmount + ".- CHF!");
}
public float getAccountBalance() {
System.out.println("User requested account balance!");
return accountBalance;
}
public void setSessionContext(SessionContext sc) {
try {
InitialContext ic = new InitialContext();
Context local = (Context) ic.lookup("java:comp/env");
Float interestrate = (Float) local.lookup("Zins");
rate = interestrate.floatValue();
System.out.println("AutomaticTellerBean context set!");
System.out.println("NEW RATE SET TO: " + rate);
} catch (javax.naming.NamingException e) {
e.printStackTrace();
}
System.out.println("AutomaticTellerBean context set!");
}
public void ejbCreate() {
System.out.println("AutomaticTellerBean created!");
}
public void ejbRemove() {
System.out.println("AutomaticTellerBean removed!");
}
public void ejbActivate() {
System.out.println("AutomaticTellerBean activated!");
}
public void ejbPassivate() {
System.out.println("AutomaticTellerBean passivated!");
}
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 154 von total 218 Seiten
J2EE – EJB 2.x
Client Application: TellerClient.java
package client;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
import ejb.Teller;
import ejb.TellerHome;
public class TellerClient {
public static void main(String[] args) {
try {
// create initial context
Context initial = new InitialContext();
// get home interface
TellerHome home = (TellerHome) PortableRemoteObject.narrow(
initial.lookup("TellerBean"), TellerHome.class);
// Get ejb object
Teller myTeller = home.create();
// deposit money on account
myTeller.depositMoney(500f);
System.out.println("your deposit:\t500");
// withdraw money on account
myTeller.withdrawMoney(250f);
System.out.println("your withdraw:\t250");
// deposit money on account
myTeller.depositMoney(350f);
System.out.println("your deposit:\t350");
// print AccountBalance(myTeller);
System.out.println("\nyou have now\t"
+ myTeller.getAccountBalance() + " on your account!");
// print current rate (all Teller Beans);
System.out.println("the rate is \t" + myTeller.getRate()
+ "% for all Teller Beans!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Output:
your deposit:
your withdraw:
your deposit:
500
250
350
you have now
the rate is
600.0 on your account!
1.0% for all Teller Beans!
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 155 von total 218 Seiten
J2EE – EJB 2.x
29.3
Lösung zu Übung ee3 : CMP 1.0 Entity Bean – Personenverwaltung
Home Interface: PersonHome.java
package ejb;
import
import
import
import
import
java.rmi.RemoteException;
java.util.Collection;
javax.ejb.EJBHome;
javax.ejb.CreateException;
javax.ejb.FinderException;
public interface PersonHome extends EJBHome {
// create und finder Methoden deklarieren
public Person create(String id, String name, String fname)
throws CreateException, RemoteException;
// entsprechender Return Type festlegen ( [0..1] -> Person )
public Person findByPrimaryKey(String key)
throws FinderException, RemoteException;
// entsprechender Return Type festlegen ([0..*] -> Collection
public Collection findAll() throws FinderException,
RemoteException;
}
Remote Interface: Person.java
package ejb;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
public interface Person extends EJBObject {
// public setter und getter Methoden der Entitäten deklarieren
public void setId(String id) throws RemoteException;
public void setName(String name) throws RemoteException;
public void setFname(String fname) throws RemoteException;
public String getName() throws RemoteException;
public String getFname() throws RemoteException;
public String getId() throws RemoteException;
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 156 von total 218 Seiten
J2EE – EJB 2.x
Bean Class: PersonBean.java
package ejb;
import
import
import
import
import
java.rmi.RemoteException;
javax.ejb.EJBException;
javax.ejb.EntityBean;
javax.ejb.EntityContext;
javax.ejb.RemoveException;
public class PersonBean implements EntityBean {
// DB felder als public attribute definieren
public String id = "";
public String name = "";
public String fname = "";
// public setter und getter Methoden der Entitäten definieren
public String getId() {return id;}
public void setId(String id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getFname() {return fname;}
public void setFname(String fname) {this.fname = fname;}
// Für jede create() Methode des Home Interfaces muss es eine
// entsprechende ejbCreate() Methode mit derselben Signatur geben
public String ejbCreate(String id, String name, String fname) {
// Belegen der Bean Attribute mit den Übergabeparameter
this.id = id;
this.name = name;
this.fname = fname;
// Rückgabewertes ist Primary_Key Attribute
return id;
}
// Für jede create() Methode des Home Interfaces muss es eine
// entsprechende ejbPostCreate() Methode mit derselben Signatur geben
public void ejbPostCreate(String id, String name, String fname) {
}
// sämliche Kontainer Lyfe Cycle Methoden müssen definiert werden
public void setEntityContext(EntityContext arg0)
throws EJBException, RemoteException {
}
public void unsetEntityContext() throws EJBException, RemoteException { }
public void ejbRemove() throws RemoveException, EJBException,
RemoteException { }
public
public
public
public
void
void
void
void
ejbActivate() throws EJBException, RemoteException { }
ejbPassivate() throws EJBException, RemoteException { }
ejbLoad() throws EJBException, RemoteException { }
ejbStore() throws EJBException, RemoteException { }
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 157 von total 218 Seiten
J2EE – EJB 2.x
Client Application: PersonClient.java
package client;
import
import
import
import
import
import
import
import
import
import
import
java.rmi.RemoteException;
java.util.Iterator;
javax.ejb.CreateException;
javax.ejb.FinderException;
javax.ejb.RemoveException;
javax.naming.Context;
javax.naming.InitialContext;
javax.naming.NamingException;
javax.rmi.PortableRemoteObject;
ejb.Person;
ejb.PersonHome;
public class PersonClient {
private static PersonHome anPersonHome = getHomeObject();
private static Person anPerson = null;
private static Iterator persons = null;
private static PersonHome getHomeObject() {
PersonHome person = null;
try {
Context initial = new InitialContext();
Object objref = initial.lookup("PersonBean");
person = (PersonHome) PortableRemoteObject.narrow(objref,
PersonHome.class);
} catch (ClassCastException ce) {ce.printStackTrace();
} catch (NamingException ne) {ne.printStackTrace();
}
return person;
}
public static void main(String[] args) {
create("100", "myName_100", "myFirstName_100");
create("101", "myName_101", "myFirstName_101");
create("102", "myName_102", "myFirstName_102");
create("103", "myName_103", "myFirstName_103");
create("104", "myName_104", "myFirstName_104");
findById("100");
modify("100", "myName_new", "myFirstName_new");
findAll();
remove("100");
remove("101");
remove("102");
remove("103");
remove("104");
}
private static void create(String id, String name, String fname) {
try {
anPerson = getHomeObject().create(id, name, fname);
} catch (RemoteException re) {re.printStackTrace();
} catch (CreateException ce) {ce.printStackTrace();
}
}
private static void findById(String id) {
try {
anPerson = getHomeObject().findByPrimaryKey(id);
System.out.println("found: " + anPerson.getId() + " "
+ anPerson.getFname() + " " + anPerson.getName());
} catch (RemoteException re) {re.printStackTrace();
} catch (FinderException fe) {fe.printStackTrace();
}
}
private static void findAll() {
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 158 von total 218 Seiten
J2EE – EJB 2.x
try {
persons = getHomeObject().findAll().iterator();
while (persons.hasNext()) {
anPerson = (Person) persons.next();
System.out.println("found: " + anPerson.getId() + " "
+ anPerson.getFname() + " " + anPerson.getName());
}
} catch (RemoteException re) {re.printStackTrace();
} catch (FinderException fe) {fe.printStackTrace();
}
}
private static void modify(String id, String name, String fname) {
try {
anPerson = getHomeObject().findByPrimaryKey(id);
anPerson.setName(name);
anPerson.setFname(fname);
} catch (RemoteException re) {re.printStackTrace();
} catch (FinderException fe) {fe.printStackTrace();
}
}
private static void remove(String id) {
try {
anPerson = getHomeObject().findByPrimaryKey(id);
anPerson.remove();
} catch (RemoteException re) {re.printStackTrace();
} catch (FinderException fe) {fe.printStackTrace();
} catch (RemoveException re) {re.printStackTrace();
}
}
}
Output:
found:
found:
found:
found:
found:
found:
100
100
101
102
103
104
myFirstName_100
myFirstName_new
myFirstName_101
myFirstName_102
myFirstName_103
myFirstName_104
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
myName_100
myName_new
myName_101
myName_102
myName_103
myName_104
Version 3.0
Seite 159 von total 218 Seiten
J2EE – EJB 2.x
29.4
Lösung zu Übung ee3.1 : Entity Bean CMP 2.0 - Kundenverwaltung
Local Home Interface: AddressLocalHone.java
package ejb.address;
import javax.ejb.*;
public interface AddressLocalHome extends EJBLocalHome {
public AddressLocal create(String street, String location)
throws CreateException;
// Methode findByPrimaryKey(...) muss deklariert werden
public AddressLocal findByPrimaryKey(String id)
throws FinderException;
}
Local Remote Interface: AddressLocal.java
package ejb.address;
import javax.ejb.*;
public interface AddressLocal extends EJBLocalObject {
public String getStreet();
public String getLocation();
}
Bean Class: AddressBean.java
AddressBean.java
package ejb.address;
import javax.ejb.*;
public abstract class AddressBean implements EntityBean {
private EntityContext context;
// abstrakte getter/setter-Methoden - PERSISTENTE FELDER
public abstract String getId();
public abstract void setId(String id);
public abstract String getStreet();
public abstract void setStreet(String street);
public abstract String getLocation();
public abstract void setLocation(String location);
public String ejbCreate(String street, String location)
throws CreateException {
System.out.println("AddressEJB: ejbCreate called");
setId(street + location);
setStreet(street);
setLocation(location);
return null;
}
public void ejbPostCreate(String street, String location) {
System.out.println("AddressEJB: ejbPostCreate called");
}
public void ejbRemove() {
System.out.println("AddressEJB: ejbRemove called");
}
public AddressBean() {
}
public void setEntityContext(EntityContext context) {
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 160 von total 218 Seiten
J2EE – EJB 2.x
System.out.println("setEntityContextCalled");
this.context = context;
}
public void unsetEntityContext() {
System.out.println("AddressEJB: unsetEntityContextCalled");
this.context = null;
}
public void ejbActivate() {
Object id = context.getPrimaryKey();
System.out.println("AddressEJB: ejbActivate called" + id);
}
public void ejbPassivate() {
System.out.println("AddressEJB: ejbPassivate called");
}
public void ejbLoad() {
System.out.println("AddressEJB: ejbLoad called");
}
public void ejbStore() {
System.out.println("AddressEJB: ejbStore called");
}
}
Local Home Interface: NameLocalHome.java
package ejb.name;
import javax.ejb.*;
public interface NameLocalHome extends EJBLocalHome {
public NameLocal create(String id, String street,
String location) throws CreateException;
// Methode findByPrimaryKey(...) muss deklariert werden
public NameLocal findByPrimaryKey(String id)
throws FinderException;
}
Local Remote Interface: NameLocal.java
package ejb.name;
import javax.ejb.EJBLocalObject;
import ejb.address.AddressLocal;
public interface NameLocal extends EJBLocalObject {
public AddressLocal getAddress();
public void setAddress(AddressLocal address);
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 161 von total 218 Seiten
J2EE – EJB 2.x
(Remote) Home Interface: NameHome.java
package ejb.name;
import java.rmi.RemoteException;
import javax.ejb.*;
public interface NameHome extends EJBHome {
public Name create(String id, String name, String fname,
String street, String location) throws CreateException,
RemoteException;
//Methode findByPrimaryKey(...) muss deklariert werden
public Name findByPrimaryKey(String id) throws FinderException,
RemoteException;
}
(Remote) Remote Interface: Name.java
package ejb.name;
import java.rmi.RemoteException;
import javax.ejb.*;
public interface Name extends EJBObject {
public String getFullName() throws RemoteException;
public String getFullAddress() throws RemoteException;
}
Bean Class: NameBean.java
package ejb.name;
import
import
import
import
import
javax.ejb.*;
javax.naming.*;
javax.rmi.PortableRemoteObject;
ejb.address.AddressLocalHome;
ejb.address.AddressLocal;
public abstract class NameBean implements EntityBean {
// Nicht PERSISTENTE FELDER
private EntityContext context;
private AddressLocalHome addressLocalHome;
// abstrakte getter/setter-Methoden - PERSISTENTE FELDER
public abstract String getId();
public abstract void setId(String id);
public abstract String getName();
public abstract void setName(String name);
public abstract String getFname();
public abstract void setFname(String fname);
//abstrakte getter/setter-Methoden - RELATIONEN
public abstract AddressLocal getAddress();
public abstract void setAddress(AddressLocal address);
public String ejbCreate(String id, String name, String fname,
String street, String location) throws CreateException {
setId(id);
setName(name);
setFname(fname);
return null;
}
public void ejbPostCreate(String id, String name, String fname,
String street, String location) throws CreateException {
System.out.println("ejbPostCreate called");
AddressLocal address = addressLocalHome.create(street, location);
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 162 von total 218 Seiten
J2EE – EJB 2.x
setAddress(address);
}
// Business Methoden des REMOTE NTERFACE
public String getFullName() {
return getName() + ", " + getFname();
}
public String getFullAddress() {
return getAddress().getStreet() + ", " + getAddress().getLocation();
}
public NameBean() {
}
// LIFECYCLE METHODEN
public void ejbRemove() {
System.out.println("ejbRemove called");
}
public void setEntityContext(EntityContext context) {
System.out.println("setEntityContextCalled");
this.context = context;
try {
Context ctx = new InitialContext();
Context localCtx = (Context) ctx.lookup("java:comp/env");
Object obj = localCtx.lookup("ejb/address");
addressLocalHome = (AddressLocalHome) PortableRemoteObject
.narrow(obj, AddressLocalHome.class);
} catch (NamingException e) {
throw new EJBException(e);
}
}
public void unsetEntityContext() {
System.out.println("unsetEntityContextCalled");
this.context = null;
}
public void ejbActivate() {
Object id = context.getPrimaryKey();
System.out.println("ejbActivate called" + id);
}
public void ejbPassivate() {
System.out.println("ejbPassivate called");
}
public void ejbLoad() {
System.out.println("ejbLoad called");
}
public void ejbStore() {
System.out.println("ejbStore called");
}
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 163 von total 218 Seiten
J2EE – EJB 2.x
Client Application: CustomerClient.java
package client;
import
import
import
import
import
javax.naming.Context;
javax.naming.InitialContext;
javax.rmi.PortableRemoteObject;
ejb.name.Name;
ejb.name.NameHome;
public class CustomerClient {
public static void main(String[] arg) {
try {
Context initial = new InitialContext();
Object objref = initial.lookup("NameBean");
NameHome home = (NameHome) PortableRemoteObject.narrow(
objref, NameHome.class);
Name cust1 = home.create("1", "name", "first", "street", "city");
Name cust2 = home.create("2", "name", "vorname", "strasse", "ort");
Name mycust1 = home.findByPrimaryKey("1");
System.out.println("found:\tNAME\t" + mycust1.getFullName()
+ "\tADDRESS\t" + mycust1.getFullAddress());
Name mycust2 = home.findByPrimaryKey("2");
System.out.println("found:\tNAME\t" + mycust2.getFullName()
+ "\tADDRESS\t" + mycust2.getFullAddress());
mycust1.remove();
mycust2.remove();
} catch (Exception ex) {
System.err.println("unexpected exception!");
ex.printStackTrace();
}
}
}
Output:
found: NAME name, first
ADDRESS street, city
found: NAME name, vorname ADDRESS strasse, ort
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 164 von total 218 Seiten
J2EE – EJB 2.x
29.5
Lösung zu Übung ee4 : DB Session Bean - Money Exchange
Home Interface: MoneyExchangeHome.java
package beans;
import javax.ejb.EJBHome;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
public interface MoneyExchangeHome extends EJBHome {
public MoneyExchange create() throws RemoteException, CreateException;
}
Remote Interafce: MoneyExchange.java
package beans;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
public interface MoneyExchange extends EJBObject {
public float change(float inAmount, String base, boolean jdbc)
throws RemoteException;
}
Bean Class: MoneyExchangeBean.java
package beans;
import
import
import
import
import
javax.ejb.SessionBean;
javax.ejb.SessionContext;
javax.naming.*;
java.sql.*;
javax.sql.*;
public class MoneyExchangeBean implements SessionBean {
private
private
private
private
private
InitialContext ictx;
DataSource ds;
Connection con;
Statement stmt;
ResultSet rs;
public float change(float inAmount, String base, boolean jdbc) {
float rate = 1.0f;
float result = 0.0f;
String SQLQuery = "SELECT rate FROM rates WHERE basecur = '" + base + '";
if (jdbc) {
//jdbc Connection to mySQL Database enterpriseeducation
final String userName = "root";
final String password = "";
final String databaseUrl = "jdbc:mysql://localhost/ee";
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException cnfe) {
System.err.println("Driver not found: " + cnfe.getMessage());
System.exit(0);
}
Connection aConnection = null;
try {
aConnection = DriverManager.getConnection(databaseUrl, userName,
password);
} catch (SQLException sqle) {
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 165 von total 218 Seiten
J2EE – EJB 2.x
System.err.println("Could not get connection to db:"
+ sqle.getMessage());
System.exit(0);
}
ResultSet aResultSet = null;
try {
Statement aStatement = aConnection.createStatement();
aResultSet = aStatement.executeQuery(SQLQuery);
if (aResultSet.next())
rate = aResultSet.getFloat("rate");
aConnection.close();
} catch (SQLException e) {
System.err.println("Could not execute statement '" + SQLQuery + "':"
+ e.getMessage());
System.exit(0);
}
} else {
// Database Connection over JNDI lookup
try {
ictx = new InitialContext();
ds = (DataSource) ictx.lookup("java:comp/env/jdbc/ExchangeTable");
con = ds.getConnection();
stmt = con.createStatement();
rs = stmt.executeQuery(SQLQuery);
if (rs.next()) rate = rs.getFloat("rate");
con.close();
} catch (Exception ex) { ex.printStackTrace(); }
}
result = inAmount * rate;
System.out.println(inAmount + "*" + rate + "=" + result + ":" + jdbc);
return result;
}
public void setSessionContext(SessionContext sc) {
System.out.println("MoneyExchangeBean context set !");
}
public
public
public
public
void
void
void
void
ejbCreate() {System.out.println("ejbCreate called !");}
ejbRemove() {System.out.println("ejbRemove called !");}
ejbActivate() {System.out.println("ejbActivate called !");}
ejbPassivate() {System.out.println("ejbPassivate called !");}
}
Deployment Descriptor: ejbejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar>
<description>Money Exchange</description>
<display-name>MoneyExchange</display-name>
<enterprise-beans>
<session>
<ejb-name>moneyexchange</ejb-name>
<home>beans.MoneyExchangeHome</home>
<remote>beans.MoneyExchange</remote>
<ejb-class>beans.MoneyExchangeBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Bean</transaction-type>
<resource-ref>
<res-ref-name>jdbc/ExchangeTable</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</session>
</enterprise-beans>
</ejb-jar>
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 166 von total 218 Seiten
J2EE – EJB 2.x
Deployment Descriptor: jboss.xml (JNDI Mapping)
<jboss>
<enterprise-beans>
<session>
<ejb-name>moneyexchange</ejb-name>
<jndi-name>ejb/moneyexchange</jndi-name>
<resource-ref>
<res-ref-name>jdbc/ExchangeTable</res-ref-name>
<jndi-name>java:/mySQLDS</jndi-name>
</resource-ref>
</session>
</enterprise-beans>
</jboss>
Client Application: MoneyExchangeClient.java
package client;
import
import
import
import
import
import
import
import
import
java.io.FileInputStream;
java.io.FileNotFoundException;
java.io.IOException;
java.util.Properties;
javax.naming.Context;
javax.naming.InitialContext;
javax.rmi.PortableRemoteObject;
beans.MoneyExchange;
beans.MoneyExchangeHome;
public class MoneyExchangeClient {
protected static Properties getInitialProperties() {
Properties property = new Properties();
try {
property.load(new FileInputStream("resources/jndi.properties"));
} catch (FileNotFoundException e) {
System.out.println("can't find file: jndi.properties");
} catch (IOException e) {e.printStackTrace();}
return property;
}
public static void main(String[] args) {
// jdbc=true : JDBC database connection
// jdbc=false : JNDI lookup database connection
final boolean jdbc = false;
final int amount = 100;
final String changeTo = "USD";
try {
// create initial context
Context initial = new InitialContext(getInitialProperties());
// get home interface
MoneyExchangeHome home = (MoneyExchangeHome) PortableRemoteObject
.narrow(initial.lookup("ejb/moneyexchange"),
MoneyExchangeHome.class);
// get ejb object
MoneyExchange aMoneyExchange = home.create();
// call methods on ejb
System.out.println(amount + " CHF are "
+ aMoneyExchange.change(amount, changeTo, jdbc) + " " + changeTo);
} catch (Exception e) {e.printStackTrace();}
}}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 167 von total 218 Seiten
J2EE – EJB 2.x
mySQL Script: create_rates_table.sql :
DROP TABLE IF EXISTS RATES;
CREATE TABLE RATES(
ID VARCHAR(10) PRIMARY KEY,
BASECUR VARCHAR(3),
RATE VARCHAR(10)
);
INSERT
INSERT
INSERT
INSERT
INTO
INTO
INTO
INTO
RATES
RATES
RATES
RATES
VALUES('1',
VALUES('2',
VALUES('3',
VALUES('4',
'USD',
'EUR',
'CHF',
'CND',
'1.25');
'1.52');
'1.00');
'1.17');
JBoss Logfile: [JBoss_HOME]\
[JBoss_HOME]\server\
erver\ee\
ee\log\
log\server.log
13:26:38,984 INFO
13:26:38,984 INFO
13:26:59,562 INFO
[STDOUT] MoneyExchangeBean context set
[STDOUT] ejbCreate called !
[STDOUT] 100.0 * 1.25 = 125.0 : false
Output:
100 CHF are 125.0 USD
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 168 von total 218 Seiten
J2EE – EJB 2.x
29.6
Lösung zu Übung ee5 : Handle auf Session Bean - AutomaticTeller
Home Interface: AutomaticTellerHome.jave
package beans;
import javax.ejb.EJBHome;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
public interface AutomaticTellerHome extends EJBHome {
public AutomaticTeller create() throws RemoteException, CreateException;
}
Remote Interface: AutomaticTeller.java
package beans;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
public interface AutomaticTeller extends EJBObject {
public void depositMoney(float amount) throws RemoteException;
public void withdrawMoney(float amount) throws RemoteException;
public float getAccountBalance() throws RemoteException;
}
Bean Class: AutomaticTellerBean.java
package beans;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
public class AutomaticTellerBean implements SessionBean {
private float accountBalance = 0.0f;
private SessionContext context;
public void depositMoney(float amount) {
accountBalance += amount;
}
public void withdrawMoney(float amount) {
accountBalance -= amount;
}
public float getAccountBalance() {
return accountBalance;
}
public void setSessionContext(SessionContext sc) {
this.context = sc;
}
public
public
public
public
void
void
void
void
ejbCreate() { }
ejbRemove() { }
ejbActivate() { }
ejbPassivate() { }
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 169 von total 218 Seiten
J2EE – EJB 2.x
Deployment Descriptor: ejbejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC
"-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"
"http://java.sun.com/dtd/ejb-jar_2_0.dtd">
<ejb-jar>
<display-name>AC</display-name>
<enterprise-beans>
<session>
<ejb-name>AutomaticTeller</ejb-name>
<home>beans.AutomaticTellerHome</home>
<remote>beans.AutomaticTeller</remote>
<ejb-class>beans.AutomaticTellerBean</ejb-class>
<session-type>Stateful</session-type>
<transaction-type>Bean</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
Deployment Descriptor: jboss.xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss>
<enterprise-beans>
<entity>
<ejb-name>AutomaticTeller</ejb-name>
<jndi-name>ejb/account</jndi-name>
</entity>
</enterprise-beans>
</jboss>
Client Applilation: AutomaticTellerClient.java
package client;
import
import
import
import
import
import
javax.ejb.*;
java.io.*;
java.rmi.*;
javax.naming.*;
java.util.Properties;
beans.*;
public class AutomaticTellerClient {
private
private
private
private
static
static
static
static
AutomaticTellerHome home;
Handle handle_1;
Handle handle_2;
Handle handle_3;
protected static Properties getInitialProperties() {
Properties property = new Properties();
try {
property.load(new FileInputStream("resources/jndi.properties"));
} catch (FileNotFoundException e) {
System.out.println("can't find file: jndi.properties");
} catch (IOException e) {
e.printStackTrace();
}
return property;
}
public static Handle erstelleAccount(AutomaticTellerHome home)
throws CreateException, RemoteException {
AutomaticTeller at = home.create();
return at.getHandle();
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 170 von total 218 Seiten
J2EE – EJB 2.x
}
public static void deposit(Handle handle, float Betrag)
throws RemoteException {
AutomaticTeller at = getRemoteObjekt(handle);
at.depositMoney(Betrag);
}
public static void withdraw(Handle handle, float Betrag)
throws RemoteException {
AutomaticTeller at = getRemoteObjekt(handle);
at.withdrawMoney(Betrag);
}
private static AutomaticTellerHome erzeugeHomeObjekt()
throws NamingException {
InitialContext ctx = new InitialContext(getInitialProperties());
Object ref = ctx.lookup("ejb/account");
home = (AutomaticTellerHome) PortableRemoteObject.narrow(ref,
AutomaticTellerHome.class);
return home;
}
private static void printAccountBalance(int id, Handle handle)
throws RemoteException {
AutomaticTeller at = getRemoteObjekt(handle);
System.out.println("ID " + id + " has : " + at.getAccountBalance()
+ " CHF!");
}
private static AutomaticTeller getRemoteObjekt(Handle handle)
throws RemoteException {
return (AutomaticTeller) javax.rmi.PortableRemoteObject.narrow(handle
.getEJBObject(), AutomaticTeller.class);
}
public static void main(String[] args) {
try {
home = erzeugeHomeObjekt();
handle_1 = erstelleAccount(home);
handle_2 = erstelleAccount(home);
handle_3 = erstelleAccount(home);
deposit(handle_1, 500f);
deposit(handle_2, 300f);
withdraw(handle_1, 100f);
withdraw(handle_2, 50f);
deposit(handle_2, 300f);
withdraw(handle_1, 100f);
withdraw(handle_3, 50f);
deposit(handle_2, 300f);
withdraw(handle_1, 100f);
withdraw(handle_2, 50f);
printAccountBalance(1, handle_1);
printAccountBalance(2, handle_2);
printAccountBalance(3, handle_3);
} catch (Exception e) { e.printStackTrace(); }
}
}
Output:
ID 1 has : 200.0 CHF!
ID 2 has : 800.0 CHF!
ID 3 has : -50.0 CHF!
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 171 von total 218 Seiten
J2EE – EJB 2.x
Client Application: speichert (Remote) Handle persistent in lokale Datei (Ausschnitt)
public static void main(String[] args) {
try {
home = erzeugeHomeObjekt();
handle_1 = erstelleAccount(home);
deposit(handle_1, 500f);
withdraw(handle_1, 100f);
printAccountBalance(1, handle_1);
// write the handle to serialized file
FileOutputStream f = new FileOutputStream("TellerHandle_1.ser");
ObjectOutputStream o = new ObjectOutputStream(f);
o.writeObject(handle_1);
o.flush();
o.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Output:
ID 1 has : 400.0 CHF!
Client Application: liest Handle von lokaler Datei und macht Buchungen (Ausschnitt)
(Ausschnitt)
public static void main(String[] args) {
try {
// read handle from file at later time
FileInputStream fi = new FileInputStream("TellerHandle_1.ser");
ObjectInputStream oi = new ObjectInputStream(fi);
//read the object from the file and cast it to a Handle
handle_1 = (Handle) oi.readObject();
oi.close();
deposit(handle_1, 500f);
withdraw(handle_1, 100f);
printAccountBalance(1, handle_1);
// write the handle to serialized file
FileOutputStream f = new FileOutputStream("TellerHandle_1.ser");
ObjectOutputStream o = new ObjectOutputStream(f);
o.writeObject(handle_1);
o.flush();
o.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Output:
ID 1 has : 800.0 CHF!
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 172 von total 218 Seiten
J2EE – EJB 2.x
29.7
Lösung zu Übung ee6 : CMP 2.0 Entity Bean - ProductShop
Home Interface: ProductHome.java
package beans;
import javax.ejb.*;
import java.rmi.RemoteException;
import java.util.Collection;
public interface ProductHome extends EJBHome {
Product create(String pid, String name, double price)
throws CreateException, RemoteException;
public Product findByPrimaryKey(ProductPK key) throws FinderException,
RemoteException;
public Collection findByName(String n) throws FinderException,
RemoteException;
public Collection findAllProducts() throws FinderException,
RemoteException;
}
Remote Interafce: Product.java
package beans;
import javax.ejb.*;
import java.rmi.RemoteException;
public interface Product extends EJBObject {
public String getName() throws RemoteException;
public void setName(String name) throws RemoteException;
public double getPrice() throws RemoteException;
public void setPrice(double price) throws RemoteException;
public String getProductID() throws RemoteException;
}
Primary Key Class: ProductPK.java
ProductPK.java
package beans;
public class ProductPK implements java.io.Serializable {
public String productID;
public ProductPK(String pid) {
productID = pid;
}
public ProductPK() {
}
public int hashCode() {
return productID.hashCode();
}
public boolean equals(Object obj) {
return ((ProductPK) obj).productID.equals(productID);
}
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 173 von total 218 Seiten
J2EE – EJB 2.x
Bean Class: ProductBean.java
package beans;
import javax.ejb.*;
public abstract class ProductBean implements EntityBean {
protected EntityContext ctx;
public ProductBean() {
}
public abstract String getName();
public abstract void setName(String name);
public abstract double getPrice();
public abstract void setPrice(double price);
public abstract String getProductID();
public abstract void setProductID(String pid);
public ProductPK ejbCreate(String pid, String n, double price)
throws CreateException {
setProductID(pid);
setName(n);
setPrice(price);
return new ProductPK(pid);
}
public void ejbPostCreate(String pid, String n, double price) { }
public void ejbActivate() { }
public void ejbPassivate() { }
public void ejbRemove() { }
public void ejbLoad() { }
public void ejbStore() { }
public void setEntityContext(EntityContext ctx) {
this.ctx = ctx;
}
public void unsetEntityContext() {
this.ctx = null;
}
}
Deployment Descriptor: ejbejb-jar.xml
<ejb-jar>
<description>Product Business Model</description>
<display-name>Product Bean</display-name>
<enterprise-beans>
<entity>
<description>Models a Productline</description>
<ejb-name>Product</ejb-name>
<home>beans.ProductHome</home>
<remote>beans.Product</remote>
<ejb-class>beans.ProductBean</ejb-class>
<persistence-type>Container</persistence-type>
<reentrant>False</reentrant>
<prim-key-class>beans.ProductPK</prim-key-class>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>ProductBean</abstract-schema-name>
<cmp-field>
<field-name>productID</field-name>
</cmp-field>
<cmp-field>
<field-name>name</field-name>
</cmp-field>
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 174 von total 218 Seiten
J2EE – EJB 2.x
<cmp-field>
<field-name>price</field-name>
</cmp-field>
<query>
<query-method>
<method-name>findByName</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<ejb-ql> SELECT OBJECT(a)
FROM ProductBean As a
WHERE a.name = ?1
</ejb-ql>
</query>
<query>
<query-method>
<method-name>findAllProducts</method-name>
<method-params>
</method-params>
</query-method>
<ejb-ql>
SELECT OBJECT(a)
FROM ProductBean As a
WHERE a.productID IS NOT NULL
</ejb-ql>
</query>
</entity>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>Product</ejb-name>
<method-intf>Remote</method-intf>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
Deployment Descriptor: jbosscmp
jbosscmpbosscmp-jdbc.xml
<jbosscmp-jdbc>
<defaults>
<datasource>java:/DefaultDS</datasource>
<datasource-mapping>mySQL</datasource-mapping>
<create-table>true</create-table>
<remove-table>true</remove-table>
</defaults>
<enterprise-beans>
<entity>
<ejb-name>Product</ejb-name>
<table-name>ProductTable</table-name>
</entity>
</enterprise-beans>
</jbosscmp-jdbc>
Client
Client Application: ProductClient.java
package client;
import
import
import
import
javax.naming.*;
javax.rmi.PortableRemoteObject;
java.util.*;
beans.*;
public class ProductClient {
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 175 von total 218 Seiten
J2EE – EJB 2.x
public static void main(String[] args) throws Exception {
ProductHome home = null;
System.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
System.setProperty("java.naming.provider.url", "localhost:1099");
try {
Context ctx = new InitialContext();
home = (ProductHome) PortableRemoteObject.narrow(ctx.lookup("Product"),
ProductHome.class);
home.create("p1", "HD", 180.50);
home.create("p2", "CPU", 220.30);
home.create("p3", "Ram", 160);
Iterator i = home.findAllProducts().iterator();
while (i.hasNext()) {
Product prod = (Product) PortableRemoteObject.narrow(i.next(),
Product.class);
System.out.println(prod.getName() + "\t" + prod.getPrice());
prod.remove();
}
} catch (Exception e) { }
}
}
Output:
HD
CPU
Ram
180.5
220.3
160.0
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 176 von total 218 Seiten
J2EE – EJB 2.x
29.8
Lösung zu Übung ee61 : CMP2.0 Local Interface - Summe aller Produkte
Local Home Interface: ProductLocalHome.java
package beans;
import javax.ejb.*;
import java.util.Collection;
public interface ProductLocalHome extends EJBLocalHome {
ProductLocal create(String pid, String name, double base)
throws CreateException;
public ProductLocal findByPrimaryKey(ProductPK key)
throws FinderException;
public Collection findByName(String n) throws FinderException;
public Collection findAllProducts() throws FinderException;
public double getAllPriceTotal();
}
Local Remote Interface: ProductLocal.java
ProductLocal.java
package beans;
import javax.ejb.*;
public interface ProductLocal extends EJBLocalObject {
public String getName();
public void setName(String name);
public double getPrice();
public void setPrice(double price);
public String getProductID();
}
Home Interface: ProductHome.java
ProductHome.java
package beans;
import javax.ejb.*;
import java.rmi.RemoteException;
import java.util.Collection;
public interface ProductHome extends EJBHome {
Product create(String pid, String name, double base)
throws CreateException, RemoteException;
public Product findByPrimaryKey(ProductPK key) throws FinderException,
RemoteException;
public Collection findByName(String n) throws FinderException,
RemoteException;
public Collection findAllProducts() throws FinderException,
RemoteException;
public double getAllPriceTotal() throws RemoteException;
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 177 von total 218 Seiten
J2EE – EJB 2.x
Remote Interface: Product.java
package beans;
import javax.ejb.*;
import java.rmi.RemoteException;
public interface Product extends EJBObject {
public String getName() throws RemoteException;
public void setName(String name) throws RemoteException;
public double getPrice() throws RemoteException;
public void setPrice(double price) throws RemoteException;
public String getProductID() throws RemoteException;
}
Primary Key Class: ProductPK.java
package beans;
public class ProductPK implements java.io.Serializable {
public String productID;
public ProductPK(String pid) {
productID = pid;
}
public ProductPK() {
}
public String toString() {
return productID.toString();
}
public int hashCode() {
return productID.hashCode();
}
public boolean equals(Object obj) {
return ((ProductPK) obj).productID.equals(productID);
}
}
Bean Class: ProductBean.java
package beans;
import javax.ejb.*;
import java.util.*;
public abstract class ProductBean implements EntityBean {
protected EntityContext ctx;
public ProductBean() {
}
public abstract String getName();
public abstract void setName(String name);
public abstract double getPrice();
public abstract void setPrice(double price);
public abstract String getProductID();
public abstract void setProductID(String pid);
public abstract Collection ejbSelectAllPrices() throws FinderException;
public double ejbHomeGetAllPriceTotal() {
double sum = 0.0;
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 178 von total 218 Seiten
J2EE – EJB 2.x
try {
Collection c = this.ejbSelectAllPrices();
Iterator i = c.iterator();
while (i.hasNext()) {
sum += ((ProductLocal) i.next()).getPrice();
}
} catch (Exception e) {
e.printStackTrace();
}
return sum;
}
public ProductPK ejbCreate(String pid, String n, double price)
throws CreateException {
setProductID(pid);
setName(n);
setPrice(price);
return new ProductPK(pid);
}
public void ejbPostCreate(String pid, String n, double price) {
}
public void setEntityContext(EntityContext ctx) {
this.ctx = ctx;
}
public void unsetEntityContext() {
this.ctx = null;
}
public void ejbActivate() { }
public void ejbPassivate() { }
public void ejbRemove() { }
public void ejbLoad() { }
public void ejbStore() { }
}
Deployment Descriptor: ejbejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD
Enterprise JavaBeans 2.0//EN"
"http://java.sun.com/j2ee/dtds/ejb-jar_2_0.dtd">
<ejb-jar>
<description>Product Business Model</description>
<display-name>Product Bean</display-name>
<enterprise-beans>
<entity>
<description>Models a productline</description>
<ejb-name>Product</ejb-name>
<home>beans.ProductHome</home>
<remote>beans.Product</remote>
<local-home>beans.ProductLocalHome</local-home>
<local>beans.ProductLocal</local>
<ejb-class>beans.ProductBean</ejb-class>
<persistence-type>Container</persistence-type>
<reentrant>False</reentrant>
<prim-key-class>beans.ProductPK</prim-key-class>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>ProductBean</abstract-schema-name>
<cmp-field>
<field-name>productID</field-name>
</cmp-field>
<cmp-field>
<field-name>name</field-name>
</cmp-field>
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 179 von total 218 Seiten
J2EE – EJB 2.x
<cmp-field>
<field-name>price</field-name>
</cmp-field>
<query>
<query-method>
<method-name>findByName</method-name>
<method-params>
<method-param>java.lang.String</method-param>
</method-params>
</query-method>
<ejb-ql> SELECT OBJECT(a) FROM ProductBean As a WHERE
a.name = ?1 </ejb-ql>
</query>
<query>
<query-method>
<method-name>findAllProducts</method-name>
<method-params>
</method-params>
</query-method>
<ejb-ql> SELECT OBJECT(a) FROM ProductBean As a WHERE
a.productID IS NOT NULL </ejb-ql>
</query>
<query>
<query-method>
<method-name>ejbSelectAllPrices</method-name>
<method-params>
</method-params>
</query-method>
<result-type-mapping>Local</result-type-mapping>
<ejb-ql> SELECT OBJECT(p) FROM ProductBean As p </ejb-ql>
</query>
</entity>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>Product</ejb-name>
<method-intf>Remote</method-intf>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
Deployment Descriptor: jbosscmp
jbosscmpsscmp-jdbc.xml
<jbosscmp-jdbc>
<defaults>
<datasource>java:/DefaultDS</datasource>
<datasource-mapping>mySQL</datasource-mapping>
<create-table>true</create-table>
<remove-table>true</remove-table>
<read-only>false</read-only>
<pk-constraint>true</pk-constraint>
</defaults>
<enterprise-beans>
<entity>
<ejb-name>Product</ejb-name>
<table-name>ProductTable</table-name>
</entity>
</enterprise-beans>
</jbosscmp-jdbc>
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 180 von total 218 Seiten
J2EE – EJB 2.x
Client Application:
Application: ProductClient.java
package client;
import
import
import
import
javax.naming.*;
javax.rmi.PortableRemoteObject;
java.util.*;
beans.*;
public class ProductClient {
public static void main(String[] args) throws Exception {
ProductHome home = null;
System.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
System.setProperty("java.naming.provider.url", "localhost:1099");
try {
Context ctx = new InitialContext();
home = (ProductHome) PortableRemoteObject.narrow(ctx.lookup("Product"),
ProductHome.class);
home.create("p1", "HD", 180.50);
home.create("p2", "CPU", 220.30);
home.create("p3", "Ram", 160);
System.out.println("Price:\t" + home.getAllPriceTotal());
Iterator i = home.findAllProducts().iterator();
while (i.hasNext()) {
Product prod = (Product) PortableRemoteObject.narrow(i.next(),
Product.class);
System.out.println(prod.getName() + "\t" + prod.getPrice());
prod.remove();
}
} catch (Exception e) {
}
}
}
Output:
Price:
HD
CPU
Ram
560.8
180.5
220.3
160.0
Der (Remote) Client muss somit nur noch "einen" Zugriff auf den EJB-Kontainer per RMI-IIOP
machen um das Total aller Produkte zu bestimmen.
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 181 von total 218 Seiten
J2EE – EJB 2.x
29.9
Lösung zu Übung ee62: BMP Entity Bean - PersonBean
Home Interface: BMPPersonHome.java
package beans;
import
import
import
import
java.rmi.RemoteException;
javax.ejb.CreateException;
javax.ejb.EJBHome;
javax.ejb.FinderException;
public interface BMPPersonHome extends EJBHome {
public BMPPerson create(int year, int month, int day, String name,
String city) throws CreateException, RemoteException;
public BMPPerson findByPrimaryKey(String name)
throws FinderException, RemoteException;
}
Remote Interface: BMPPerson.java
package beans;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
public interface BMPPerson extends EJBObject {
public int getAge() throws RemoteException;
public String getCity() throws RemoteException;
public void setCity(String city) throws RemoteException;
}
Bean Class: BMPPersonBean.java
package beans;
import
import
import
import
import
import
import
import
java.rmi.RemoteException;
java.sql.Connection;
java.sql.DriverManager;
java.sql.ResultSet;
java.sql.SQLException;
java.sql.Statement;
java.util.Calendar;
java.util.GregorianCalendar;
import
import
import
import
import
import
javax.ejb.CreateException;
javax.ejb.EJBException;
javax.ejb.EntityBean;
javax.ejb.EntityContext;
javax.ejb.FinderException;
javax.ejb.RemoveException;
public class BMPPersonBean implements EntityBean {
//persistent fields
private String name;
private GregorianCalendar birthdate;
private String city;
public BMPPersonBean() { }
//methods which are part of the remote interface
public int getAge() {
return ( new GregorianCalendar().get(Calendar.YEAR)
- birthdate.get(Calendar.YEAR));
}
public String getCity() {
return city;
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 182 von total 218 Seiten
J2EE – EJB 2.x
public void setCity(String city) throws RemoteException {
if (city.length() <= 30) {
this.city = city;
} else {
throw new RemoteException("cannot update city");
}
}
// -----------------------------------------------------------------public String ejbCreate(
int year,
int month,
int day,
String name,
String city)
throws CreateException {
if (year > 1900
&& month >= 1
&& month <= 12
&& day >= 1
&& day <= 31
&& name.length() <= 20
&& city.length() <= 30) {
this.name = name;
this.birthdate = new GregorianCalendar(year, month, day);
this.city = city;
Statement stmt = getStatement();
String s =
new String(
"INSERT INTO BMPPERSON VALUES ('"
+ name
+ "','"
+ birthdate.get(Calendar.YEAR)
+ "-"
+ birthdate.get(Calendar.MONTH)
+ "-"
+ birthdate.get(Calendar.DATE)
+ "',"
+ "'"
+ city
+ "'"
+ ");");
try {
stmt.executeUpdate(s);
} catch (SQLException e) {
e.printStackTrace();
}
} else {
throw new CreateException("Invalid values supplied");
}
return name;
}
public void ejbPostCreate(
int year,
int month,
int day,
String name,
String city) {
}
// -----------------------------------------------------------------public int ejbHomeGetAge() {
return 0;
}
public String ejbHomeGetCity() {
return new String();
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 183 von total 218 Seiten
J2EE – EJB 2.x
//
//
public void ejbHomeSetCity(String city) {
}
public Statement getStatement() {
try {
Class.forName("org.gjt.mm.mysql.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Connection con = null;
try {
con =
(Connection) DriverManager.getConnection(
"jdbc:mysql://localhost/ee",
"root",
"");
} catch (SQLException e1) {
e1.printStackTrace();
}
try {
return ((Statement) con.createStatement());
} catch (SQLException e2) {
e2.printStackTrace();
}
return null; //never gets here
}
// -----------------------------------------------------------------public void setEntityContext(EntityContext ectx)
throws EJBException, RemoteException {
}
public void unsetEntityContext() throws EJBException, RemoteException {
}
public void ejbRemove()
throws RemoveException, EJBException, RemoteException {
Statement stmt = getStatement();
String s = new String(
"DELETE FROM BMPPERSON WHERE Name='" + name + "';");
try {
stmt.executeUpdate(s);
} catch (SQLException e) {
e.printStackTrace();
}
}
public void ejbActivate() throws EJBException, RemoteException {
}
public void ejbPassivate() throws EJBException, RemoteException {
}
public void ejbLoad() throws EJBException, RemoteException {
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 184 von total 218 Seiten
J2EE – EJB 2.x
public void ejbStore() throws EJBException, RemoteException {
Statement stmt = getStatement();
String s =
new String(
"UPDATE BMPPERSON SET Name='"
+ name
+ "', Birthdate='"
+ birthdate.get(Calendar.YEAR)
+ "-"
+ birthdate.get(Calendar.MONTH)
+ "-"
+ birthdate.get(Calendar.DATE)
+ "', City = '"
+ city
+ "' WHERE Name = '"
+ name
+ "';");
try {
stmt.executeUpdate(s);
} catch (SQLException e) {
e.printStackTrace();
}
}
public String ejbFindByPrimaryKey(String name) throws FinderException {
Statement stmt = getStatement();
String s =
new String("SELECT Name FROM BMPPERSON WHERE Name='" + name + "';");
try {
ResultSet rs = stmt.executeQuery(s);
rs.first();
return rs.getString("Name");
} catch (SQLException e) {
e.printStackTrace();
}
return null; //never gets here
}
}
Client Application: BMPPersonClient.java
package client;
import java.rmi.RemoteException;
import java.util.Properties;
import
import
import
import
import
import
import
javax.ejb.CreateException;
javax.ejb.FinderException;
javax.ejb.RemoveException;
javax.naming.Context;
javax.naming.InitialContext;
javax.naming.NamingException;
javax.rmi.PortableRemoteObject;
import beans.BMPPerson;
import beans.BMPPersonHome;
public class BMPPersonClient{
protected static Properties getInitialProperties() {
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
props.put(Context.PROVIDER_URL, "localhost:1099");
props.put("java.naming.factory.url.pkgs",
"org.jboss.naming;org.jnp.interfaces");
return props;
}
public static void main(String[] args) {
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 185 von total 218 Seiten
J2EE – EJB 2.x
try {
Context initial = new InitialContext(getInitialProperties());
Object objRef = initial.lookup("ejb/bmpperson");
BMPPersonHome home = (BMPPersonHome) PortableRemoteObject.narrow(
objRef,BMPPersonHome.class);
BMPPerson p1 = home.create(1981, 1, 1, "Leonie", "cityA");
BMPPerson p2 = home.create(1972, 2, 2, "Lukas", "cityB");
System.out.println("Leonies Alter: " + p1.getAge());
System.out.println("Leonies PK: "+p1.getPrimaryKey());
p1.remove();
BMPPerson p3 = home.findByPrimaryKey("Lukas");
System.out.println( "Lukas wohnt in: " + p3.getCity());
p3.setCity("cityC");
System.out.println("Lukas neuer Wohnort: " + p3.getCity());
System.out.println("Lukas alter Wohnort: " + p2.getCity());
System.out.println(
"sind p2 und p3 das gleiche Java Object ? "+ (p2==p3));
System.out.println(
"sind p2 und p3 das gleiche EJBObject ? "+p2.isIdentical(p3));
} catch (NamingException
ne.printStackTrace();
} catch (RemoteException
re.printStackTrace();
} catch (CreateException
ce.printStackTrace();
} catch (RemoveException
e.printStackTrace();
} catch (FinderException
e.printStackTrace();
}
ne) {
re) {
ce) {
e) {
e) {
}
}
Output:
Leonies Alter: 25
Leonies PK: Leonie
Lukas wohnt in: cityB
Lukas neuer Wohnort: cityC
Lukas alter Wohnort: cityC
sind p2 und p3 das gleiche Java Object ? false
sind p2 und p3 das gleiche EJBObject ? true
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 186 von total 218 Seiten
J2EE – EJB 2.x
29.10
Lösung zu Übung ee7 : Servlet - WorkingServlet
Servlet: WorkingServlet.java
package mypackage;
import
import
import
import
import
import
java.io.IOException;
java.io.Writer;
javax.servlet.ServletException;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
/**
* Sample Servlet showing a HTML page with a message.
* Call Servlet via: http://localhost:8080/ee7/start
*/
public class HelloServlet extends HttpServlet {
public void service(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws IOException, ServletException {
// Get Writer object of response
Writer aResponseWriter = inResponse.getWriter();
// Specify content as HTML page
inResponse.setContentType("text/html");
// Write HTML content to response
aResponseWriter
.write("<html><head><title>1st WorkingServlet</title></head>\n");
aResponseWriter
.write("<html><head><title>1st WorkingServlet</title></head>\n");
aResponseWriter.write("<body>\n");
aResponseWriter
.write("<h1>My first SampleServlet is working great!</h1>\n");
aResponseWriter.write("</body></html>");
// Force sending of response
aResponseWriter.flush();
}
}
-
notwendige Library :
servlet.jar
Source Path :
.../WEB-INF/src/
Build Path
.../WEB-INF/classes/
server.xml
Die Datei server.xml automatisch konfigurieren mit:
Kontext-Menü > Tomcat Projekt >
Kontext in server.xml aktualisieren
ergibt folgenden Eintrag in der Datei
[TOMCAT_HOME]/conf/server.xml:
<Context path="/ee7" reloadable="true"
docBase="C:\sbb\workspace\ee7"
workDir="C:\sbb\workspace\ee7\work" />
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 187 von total 218 Seiten
J2EE – EJB 2.x
web.xml
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Servlet Test Application</display-name>
<description>Schreibt eine Begrüssung</description>
<servlet>
<servlet-name>Hello</servlet-name>
<servlet-class>mypackage.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Hello</servlet-name>
<url-pattern>/start</url-pattern>
</servlet-mapping>
</web-app>
Browser:
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 188 von total 218 Seiten
J2EE – EJB 2.x
29.11
Lösung zu Übung ee71: Servlet mit EJB Client - Web Shop
Servlet: BasketServlet.java
package servlet;
import
import
import
import
import
import
import
import
import
import
java.io.IOException;
java.io.Writer;
java.util.Collection;
java.util.Iterator;
javax.servlet.ServletException;
javax.servlet.http.HttpServlet;
javax.servlet.http.HttpServletRequest;
javax.servlet.http.HttpServletResponse;
beans.Product;
client.ProductClientBean;
public class BasketServlet extends HttpServlet {
public void service(HttpServletRequest inRequest,
HttpServletResponse inResponse) throws IOException, ServletException {
// Get Writer object of response
Writer aResponseWriter = inResponse.getWriter();
// Specify content as HTML page
inResponse.setContentType("text/html");
String headContent
= "<html>\n<head>\n <title> Products From Shop"
+ "</title>\n\t</head>\n <body>\n";
String outContent
= "<table cellspacing=\"5\" cellpadding=\"5\""
+ "border=\"0\" bgcolor=\"#dddddd\">\n"
+ "<tr>\n"
+ "<td colspan=\"3\">All Current Products in Shop</td>\n"
+ "</tr>\n" + "<tr bgcolor=\"#bbbbbb\">\n";
Product aProduct = null;
ProductClientBean pcb = new ProductClientBean();
try {
Collection c = pcb.getProducts();
Iterator p = c.iterator();
outContent += " <td><b>Id</td><td>Name</b></td><td>Price</b></td>\n"
+ "</tr>\n" + "<tr bgcolor=\"#cccccc\">\n ";
while (p.hasNext()) {
aProduct = (Product) p.next();
outContent += "<td>" + aProduct.getProductID() + "</td>";
outContent += "<td>" + aProduct.getName() + "</td>";
outContent += "<td>" + aProduct.getPrice() + "</td>\n";
outContent += "</tr>\n<tr bgcolor=\"#cccccc\">\n";
}
} catch (Exception e) {
Throwable t = e;
while (t != null) {
outContent += " <td><b>Error</td><td>Class</b>"
+ "</td><td>Message</b></td>\n"
+ "</tr>\n" + "<tr bgcolor=\"#cccccc\">\n";
outContent += "<td>Error:</td>";
outContent += "<td>" + t.getClass().getName() + "</td>";
outContent += "<td>" + t.getMessage() + "</td>\n";
outContent += "</tr>\n<tr bgcolor=\"#cccccc\">\n ";
t = t.getCause();
}
}
outContent += " \n</tr>\n</table>\n";
String buttonContent = "</body>\n</html>";
// Write HTML content to response
aResponseWriter.write(headContent + outContent + buttonContent);
aResponseWriter.flush();
}
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 189 von total 218 Seiten
J2EE – EJB 2.x
Java Bean (Tier(Tier-Interface): ProductClientBean.java
package client;
import
import
import
import
import
java.util.Collection;
javax.naming.Context;
javax.naming.InitialContext;
javax.rmi.PortableRemoteObject;
beans.ProductHome;
public class ProductClientBean {
public Collection getProducts() throws Exception {
System.setProperty("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
System.setProperty("java.naming.provider.url", "localhost:1099");
ProductHome home = null;
Collection products = null;
try {
Context ctx = new InitialContext();
home = (ProductHome) PortableRemoteObject.narrow(ctx.lookup("Product"),
ProductHome.class);
products = home.findAllProducts();
} catch (Exception e) {
System.err.println("Error establishing bean connection");
Throwable t = e;
while (t != null) {
System.err.println("Type: " + t.getClass().getName());
System.err.println("Message: " + t.getMessage());
t = t.getCause();
}
}
return products;
}
}
Deployment Descriptor: web.xml
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Product Shop</display-name>
<description>
Listet alle Producte auf
</description>
<servlet>
<servlet-name>Products</servlet-name>
<servlet-class>
servlet.BasketServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Products</servlet-name>
<url-pattern>/shop</url-pattern>
</servlet-mapping>
</web-app>
Die Klassen des Product EJB sind identisch zu Übung ee6.
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 190 von total 218 Seiten
J2EE – EJB 2.x
Browser:
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 191 von total 218 Seiten
J2EE – EJB 2.x
29.12
Lösung zu Übung ee8 : JSP mit JDBC, Taglib und EJB - Währungsrechner
JavaServerPage: Currency.jsp
<%--@ page errorPage="CurrencyNA.jsp"--%>
<jsp:useBean id="currency"
class="client.MoneyExchangeClientBean"
scope="page"/>
<%
String amount = "";
String from = "1";
String to = "1";
if (request.getParameter("amount") != null)
amount = request.getParameter("amount");
if (request.getParameter("from") != null)
from = request.getParameter("from");
if (request.getParameter("to") != null)
to = request.getParameter("to");
%>
<html>
<head>
<title>Währungsumrechner</title>
</head>
<body>
<h1>Währungsumrechner (ee8)</h1>
<p>Betrag und Währungen auswählen:</p>
<form action="Currency.jsp">
<input type="text" name="amount" size="10" value=<%=amount%> >
<select name="from">
<jsp:include page="/src/jsp/Currency_DBTags.jsp"/>
</select>
umrechnen in
<select name="to">
<jsp:include page= "/src/jsp/Currency_DBTags.jsp"/>
</select>
<input type=SUBMIT value="Go!">
</form>
<p><font size='1'>SELECT erstellt mit DB-TAGS  
( see : Currency_DBTags.jsp )</font></p>
<%@ include file="Currency_JDBC.jsp"%>
<hr>
<table>
<tr>
<td width = 300>Umrechnungskurs ist:</td>
<td width = 100><b><%=exch_rate%></b></td>
<td width = 600><font size='1'>  
( see : Currency_JDBC.jsp )</fomt> </td>
</tr>
</table>
<hr>
<% if (amount.equals("")) { %>
Bitte Betrag eingeben !
<% } else { %>
<table>
<tr>
<td width = 300>Berechnetes Resultat:</td>
<td width = 100><b><%= currency.calculate(amount, exch_rate) %></b></td>
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 192 von total 218 Seiten
J2EE – EJB 2.x
<td width = 600><font size='1'>  
( see : MoneyExchangeBean )</fomt> </td>
</tr>
</table>
<% } %>
<hr>
</body>
</html>
JavaServerPage: Currency_JDBC.jsp
<%@ page import="java.sql.*" %>
<% String jdbc_amount = "0";
String jdbc_from = "1";
String jdbc_to = "1";
if (request.getParameter("amount") != null)
jdbc_amount = request.getParameter("amount");
if (request.getParameter("from") != null)
jdbc_from = request.getParameter("from");
if (request.getParameter("to") != null)
jdbc_to = request.getParameter("to");
String SQLQuerry = "";
ResultSet rs = null;
String base = "";
float exch_rate = 0.0f;
String con = "jdbc:mysql://localhost:3306/ee";
String user = "root";
String pass = "";
Class.forName("org.gjt.mm.mysql.Driver");
Connection connection = DriverManager.getConnection(con,user,pass);
Statement statement = connection.createStatement();
SQLQuerry = "select basecur,rate from rates where id = " + jdbc_from;
rs = statement.executeQuery(SQLQuerry);
rs.next();
base = rs.getString(1);
exch_rate = rs.getFloat(2);
SQLQuerry = "select basecur,rate from rates where id = " + jdbc_to;
rs = statement.executeQuery(SQLQuerry);
rs.next();
base = rs.getString(1);
exch_rate = exch_rate / rs.getFloat(2);
statement.close();
connection.close();
%>
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 193 von total 218 Seiten
J2EE – EJB 2.x
JavaServerPage: Currency_DBTags.jsp
<%@ taglib uri="http://jakarta.apache.org/taglibs/dbtags" prefix="sql" %>
<%-- open a database connection --%>
<sql:connection id="conn1">
<sql:url>jdbc:mysql://localhost:3306/ee</sql:url>
<sql:driver>org.gjt.mm.mysql.Driver</sql:driver>
<sql:userId>root</sql:userId>
<sql:password> </sql:password>
</sql:connection>
<%-- get the requestet parameter --%>
<sql:statement id="stmt1" conn="conn1">
<sql:query>
select id, basecur from rates
order by 2
</sql:query>
<sql:resultSet id="rset1">
<option value="<sql:getColumn position="1"/>">
<sql:getColumn position="2"/>
</sql:resultSet>
</sql:statement>
<%-- close a database connection --%>
<sql:closeConnection conn="conn1"/>
JavaServerPage:
JavaServerPage: CurrencyNA.jsp
<%@ page isErrorPage="true" %>
<html>
<head>
<title>Ausführungsfehler</title>
</head>
<body>
<h1>Fehler</h1>
<p>Service ist nicht verfügbar.</p>
</body>
</html>
Java Bean (Tier(Tier-Interface): MoneyExchangeClientBean.java
MoneyExchangeClientBean.java
package client;
import
import
import
import
import
import
javax.naming.Context;
javax.naming.InitialContext;
javax.rmi.PortableRemoteObject;
beans.MoneyExchange;
beans.MoneyExchangeHome;
java.util.Properties;
public class MoneyExchangeClientBean {
protected static Properties getInitialProperties() {
Properties props = new Properties();
props.put("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
props.put("java.naming.provider.url", "jnp://localhost:1099");
props.put("java.naming.factory.url.pkgs",
"org.jboss.naming:org.jnp.interfaces");
return props;
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 194 von total 218 Seiten
J2EE – EJB 2.x
public String calculate(String betrag, float exchangerate) {
float amount = Float.parseFloat(betrag);
String result = "";
try {
Context initial = new InitialContext(getInitialProperties());
MoneyExchangeHome home = (MoneyExchangeHome) PortableRemoteObject
.narrow(initial.lookup("moneyexchange"), MoneyExchangeHome.class);
MoneyExchange aMoneyExchange = home.create();
result = aMoneyExchange.change(amount, exchangerate);
} catch (Exception e) {
e.printStackTrace();
}
// Trace Information - Erscheint auf Tomcat Konsole
System.out.println("Resultat : " + result);
return result;
}
}
Home Interface: MoneyExchangeHome.java
MoneyExchangeHome.java
package beans;
import javax.ejb.EJBHome;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
public interface MoneyExchangeHome extends EJBHome {
public MoneyExchange create() throws RemoteException, CreateException;
}
Remote Interface: MoneyExchange.java
package beans;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
public interface MoneyExchange extends EJBObject {
public String change(float amount, float rate) throws RemoteException;
}
Bean Class: MoneyExchangeBean.java
MoneyExchangeBean.java
package beans;
import javax.ejb.*;
import javax.naming.*;
public class MoneyExchangeBean implements SessionBean {
private InitialContext ictx;
public String change(float amount, float rate) {
System.out.println("amount : " + amount);
System.out.println("amount : " + rate);
System.out.println("amount : " + amount * rate);
return "" + (amount * rate);
}
public
public
public
public
public
void
void
void
void
void
setSessionContext(SessionContext sc) { }
ejbCreate() { }
ejbRemove() { }
ejbActivate() { }
ejbPassivate() { }
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 195 von total 218 Seiten
J2EE – EJB 2.x
Deployment Descriptor: web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
<description> Example web application illustrating the use of
tags in the DBTags custom tag library, from the
JAKARTA-TAGLIBS project. </description>
<welcome-file-list>
<welcome-file>src/jsp/Currency.jsp</welcome-file>
</welcome-file-list>
<taglib>
<taglib-uri>
http://jakarta.apache.org/taglibs/dbtags</taglib-uri>
<taglib-location>
/WEB-INF/taglibs-dbtags.tld</taglib-location>
</taglib>
</web-app>
Deployment Descriptor: ejbejb-jar.xml
<ejb-jar>
<description>
Money Exchange
</description>
<display-name>
MoneyExchange
</display-name>
<enterprise-beans>
<session>
<ejb-name>
moneyexchange
</ejb-name>
<home>
beans.MoneyExchangeHome
</home>
<remote>
beans.MoneyExchange
</remote>
<ejb-class>
beans.MoneyExchangeBean
</ejb-class>
<session-type>
Stateless
</session-type>
<transaction-type>
Bean
</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 196 von total 218 Seiten
J2EE – EJB 2.x
29.13
Lösung zu Übung ee91 : EJB Security
Home Interface: SecurityTestHome.java
package beans;
import javax.ejb.EJBHome;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
public interface SecurityTestHome extends EJBHome {
public SecurityTest create() throws RemoteException, CreateException;
}
Remote Interface: SecurityTest.java
package beans;
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
import javax.security.auth.login.FailedLoginException;
public interface SecurityTest extends EJBObject {
public String doSomething(String role) throws
FailedLoginException, RemoteException;
public String getPrincipalInfo(String role) throws RemoteException;
}
Bean Class: SecurityTestBean.java
package beans;
import java.security.Principal;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.security.auth.login.FailedLoginException;
public class SecurityTestBean implements SessionBean {
private SessionContext sc;
public
public
public
public
public
void
void
void
void
void
ejbCreate() { System.out.println("ejbCreate() called"); }
ejbActivate() { System.out.println("ejbActivate() called"); }
ejbPassivate() { System.out.println("ejbPassivate() called");}
ejbRemove() { System.out.println("ejbRemove() called"); }
setSessionContext(SessionContext context) { sc = context; }
public String doSomething(String role) throws FailedLoginException {
Principal p = sc.getCallerPrincipal();
if (sc.isCallerInRole(role)) {
return "Principal (" + p + ") identifies as Role \"" + role + "\"";
} else {
throw new FailedLoginException("wrong Principal / wrong Role");
}
}
public String getPrincipalInfo(String role) {
Principal p = sc.getCallerPrincipal();
boolean r = sc.isCallerInRole(role);
System.out.println("callerPrincipal=" + p);
System.out.println("callerRole(" + role + ") = " + r);
return "Principal = " + p + " Role(" + role +") = " + r;
}
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 197 von total 218 Seiten
J2EE – EJB 2.x
Deployment Descriptor: ejbejb-jar.xml
<ejb-jar>
<display-name>JaasSecurityTest</display-name>
<enterprise-beans>
<session>
<ejb-name>EJBSecurity</ejb-name>
<home>beans.SecurityTestHome</home>
<remote>beans.SecurityTest</remote>
<ejb-class>beans.SecurityTestBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<security-role-ref>
<role-name>admin</role-name>
<role-link>staff</role-link>
</security-role-ref>
<security-role-ref>
<role-name>user</role-name>
<role-link>customer</role-link>
</security-role-ref>
</session>
</enterprise-beans>
<assembly-descriptor>
<security-role>
<role-name>guest</role-name>
</security-role>
<security-role>
<role-name>customer</role-name>
</security-role>
<security-role>
<role-name>staff</role-name>
</security-role>
<method-permission>
<role-name>guest</role-name>
<method>
<ejb-name>EJBSecurity</ejb-name>
<method-intf>Home</method-intf>
<method-name>*</method-name>
</method>
<method>
<ejb-name>EJBSecurity</ejb-name>
<method-intf>Remote</method-intf>
<method-name>getPrincipalInfo</method-name>
</method>
</method-permission>
<method-permission>
<role-name>customer</role-name>
<method>
<ejb-name>EJBSecurity</ejb-name>
<method-intf>Home</method-intf>
<method-name>*</method-name>
</method>
<method>
<ejb-name>EJBSecurity</ejb-name>
<method-intf>Remote</method-intf>
<method-name>getPrincipalInfo</method-name>
</method>
<method>
<ejb-name>EJBSecurity</ejb-name>
<method-intf>Remote</method-intf>
<method-name>doSomething</method-name>
</method>
</method-permission>
<method-permission>
<role-name>staff</role-name>
<method>
<ejb-name>EJBSecurity</ejb-name>
<method-name>*</method-name>
</method>
</method-permission>
</assembly-descriptor>
</ejb-jar>
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 198 von total 218 Seiten
J2EE – EJB 2.x
Client Application:
Application: SecurityTestClient.java
package client;
import
import
import
import
import
import
java.io.FileInputStream;
java.util.Properties;
javax.naming.InitialContext;
javax.security.auth.callback.*;
javax.security.auth.login.LoginContext;
beans.*;
public class SecurityTestClient {
static class AppCallbackHandler implements CallbackHandler {
private String username;
private char[] password;
public AppCallbackHandler(String username, char[] password) {
this.username = username;
this.password = password;
}
public void handle(Callback[] callbacks) throws java.io.IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
NameCallback nc = (NameCallback) callbacks[i];
nc.setName(username);
} else if (callbacks[i] instanceof PasswordCallback) {
PasswordCallback pc = (PasswordCallback) callbacks[i];
pc.setPassword(password);
} else {
throw new UnsupportedCallbackException(callbacks[i],"Unrecognized Callback");
}
}
}
}
public static void main(String args[]) throws Exception {
/* usernames : admin,
* passwords : admin,
* roles
: staff,
*/
String
String
String
char[]
user,
user,
customer,
guest
guest
guest
name = "admin";
pass = "admin";
role = "staff";
password = pass.toCharArray();
System.setProperty("java.security.auth.login.config",
"[JBoss-Home]/client/auth.conf");
//
//
//
//
[JBoss-Home]/client/auth.conf:
SecurityTest {
org.jboss.security.ClientLoginModule required debug=false;
};
try {
AppCallbackHandler handler = new AppCallbackHandler(name, password);
LoginContext lc = new LoginContext("SecurityTest", handler);
lc.login();
Properties env = new Properties();
env.load(new FileInputStream("resources/jndi.properties"));
InitialContext initial = new InitialContext(env);
SecurityTestHome home = (SecurityTestHome) initial.lookup("ejb/Security");
SecurityTest st = home.create();
System.out.println(
"Principal : " + name + " wants to getPrincipalInfo : " +
st.getPrincipalInfo(role));
System.out.println(
"Principal : " + name + " wants to doSomething
: " + st.doSomething(role));
st.remove();
lc.logout();
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 199 von total 218 Seiten
J2EE – EJB 2.x
} catch (Exception e) {
System.err.println("Failed :");
Throwable t = e;
while (t != null) {
System.err.println("Type: " + t.getClass().getName());
System.err.println("Message: " + t.getMessage());
System.err.println("-----");
t = t.getCause();
}
}
}
}
User Policy File: users.properties
admin=admin
user=user
guest=guest
Role Policy File: roles.properties
admin=staff
guest=guest
user=customer
Deployment Descriptor: jboss.xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss>
<!-- All bean containers use this security manager by default -->
<security-domain>java:/jaas/EJBSecurityTest</security-domain>
<enterprise-beans>
<session>
<ejb-name>EJBSecurity</ejb-name>
<jndi-name>ejb/Security</jndi-name>
</session>
</enterprise-beans>
</jboss>
Authentification Configuration File: [JBoss[JBoss-Home]/client/auth.conf
...
other {
// jBoss LoginModule
org.jboss.security.ClientLoginModule
;
required
// Put your login modules that need jBoss here
};
EJBSecurityTest {
// EJBSecurityTest JBoss Login MOdule
org.jboss.security.ClientLoginModule required debug=false;
};
Output:
Principal : admin wants to getPrincipalInfo :
Principal = admin Role(staff) = true
Principal : admin wants to doSomething
:
Principal (admin) identifies as Role "staff"
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 200 von total 218 Seiten
J2EE – EJB 2.x
29.14
Lösung zu Übung ee10 : EJB Transaction
Home Interface: AccountHome.java
package beans;
import java.rmi.RemoteException;
import javax.ejb.*;
public interface AccountHome extends EJBHome {
public Account create(String id, String owner) throws RemoteException,
CreateException;
public Account findByPrimaryKey(String id) throws FinderException,
RemoteException;
}
Remote Interface: Account.java
package beans;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Account extends EJBObject {
public double getBalance() throws RemoteException;
public String getOwner() throws RemoteException;
public void deposit(double amount) throws RemoteException,
IllegalOperationException;
public void withdraw(double amount) throws RemoteException,
IllegalOperationException;
}
Bean Class: AccountBean.java
package beans;
import
import
import
import
import
import
import
import
import
java.sql.*;
javax.ejb.CreateException;
javax.ejb.EJBException;
javax.ejb.EntityBean;
javax.ejb.EntityContext;
javax.ejb.FinderException;
javax.ejb.NoSuchEntityException;
javax.ejb.ObjectNotFoundException;
javax.naming.NamingException;
public class AccountBean implements EntityBean {
private
private
private
private
private
EntityContext context;
Connection con;
String id;
String owner;
double balance;
public double getBalance() {
log();
return balance;
}
public String getOwner() {
log();
return owner;
}
public void deposit(double amount) throws IllegalOperationException {
log();
balance += amount;
if (balance < 0)
throw new IllegalOperationException("balance becomes less than 0");
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 201 von total 218 Seiten
J2EE – EJB 2.x
public void withdraw(double amount) throws IllegalOperationException {
log();
balance -= amount;
if (balance < 0)
throw new IllegalOperationException("balance becomes less than 0");
}
public String ejbCreate(String id, String owner) throws CreateException {
log();
try {
insertRow(id, owner, 0.0);
} catch (Exception ex) {
throw new EJBException("ejbCreate: " + ex.getMessage());
}
this.id = id;
this.owner = owner;
return id;
}
public String ejbFindByPrimaryKey(String primaryKey) throws FinderException {
log();
boolean result;
try {
result = selectByPrimaryKey(primaryKey);
} catch (Exception e) {
throw new EJBException("ejbFindByPrimaryKey: " + e.getMessage());
}
if (result) {
return primaryKey;
} else {
throw new ObjectNotFoundException("Row for id " + primaryKey + " not found.");
}
}
public void ejbRemove() {
log();
try {
deleteRow(id);
} catch (Exception ex) {
throw new EJBException("ejbRemove: " + ex.getMessage());
}
}
public void setEntityContext(EntityContext context) {
log();
this.context = context;
try {
makeConnection();
} catch (Exception ex) {
throw new EJBException("Unable to connect to database. " + ex.getMessage());
}
}
public void unsetEntityContext() {
log();
try {
con.close();
} catch (SQLException e) {
throw new EJBException("unsetEntityContext: " + e.getMessage());
}
}
public void ejbActivate() {
log();
id = (String) context.getPrimaryKey();
}
public void ejbPassivate() {
log();
id = null;
}
public void ejbLoad() {
log();
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 202 von total 218 Seiten
J2EE – EJB 2.x
try {
loadRow();
} catch (Exception ex) {
throw new EJBException("ejbLoad: " + ex.getMessage());
}
}
public void ejbStore() {
log();
try {
storeRow();
} catch (Exception ex) {
throw new EJBException("ejbStore: " + ex.getMessage());
}
}
public void ejbPostCreate(String id, String owner) { log(); }
public AccountBean() { log(); }
private void log() {
StackTraceElement ste = new Exception().getStackTrace()[1];
System.out.println(this + "[" + Thread.currentThread().hashCode() + "]: "
+ ste.getMethodName());
}
/** ********************* Database Routines ************************ */
private void makeConnection() throws NamingException, SQLException {
log();
final String userName = "root";
final String password = "";
final String databaseUrl = "jdbc:mysql://localhost/ee";
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException cnfe) {
System.err.println("Driver not found: " + cnfe.getMessage());
}
try {
con = DriverManager.getConnection(databaseUrl, userName, password);
} catch (SQLException sqle) {
throw new SQLException("Could not get connection to db:" + sqle.getMessage());
}
}
private void insertRow(String id, String owner, double balance)
throws SQLException {
String insertStatement = "insert into accounts values ( ? , ? , ? )";
PreparedStatement prepStmt = con.prepareStatement(insertStatement);
prepStmt.setString(1, id);
prepStmt.setString(2, owner);
prepStmt.setDouble(3, balance);
prepStmt.executeUpdate();
prepStmt.close();
}
private void deleteRow(String id) throws SQLException {
String deleteStatement = "delete from accounts where id = ? ";
PreparedStatement prepStmt = con.prepareStatement(deleteStatement);
prepStmt.setString(1, id);
prepStmt.executeUpdate();
prepStmt.close();
}
private boolean selectByPrimaryKey(String primaryKey) throws SQLException {
log();
String selectStatement = "select id from accounts where id = ? ";
PreparedStatement prepStmt = con.prepareStatement(selectStatement);
prepStmt.setString(1, primaryKey);
ResultSet rs = prepStmt.executeQuery();
boolean result = rs.next();
prepStmt.close();
return result;
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 203 von total 218 Seiten
J2EE – EJB 2.x
private void loadRow() throws SQLException {
String selectStatement = "select owner, balance from accounts where id = ? ";
PreparedStatement prepStmt = con.prepareStatement(selectStatement);
prepStmt.setString(1, this.id);
ResultSet rs = prepStmt.executeQuery();
if (rs.next()) {
this.owner = rs.getString(1);
this.balance = rs.getDouble(2);
prepStmt.close();
} else {
prepStmt.close();
throw new NoSuchEntityException("Row for id " + id
+ " not found in database.");
}
}
private void storeRow() throws SQLException {
String updateStatement = "update accounts set owner = ? , balance = ? where id = ?";
PreparedStatement prepStmt = con.prepareStatement(updateStatement);
prepStmt.setString(1, owner);
prepStmt.setDouble(2, balance);
prepStmt.setString(3, id);
int rowCount = prepStmt.executeUpdate();
prepStmt.close();
if (rowCount == 0) {
throw new EJBException("Storing row for id " + id + " failed.");
}
}
}
Home Interface: BankHome.java
package beans;
import java.rmi.RemoteException;
import javax.ejb.*;
public interface BankHome extends EJBHome {
public Bank create() throws RemoteException, CreateException;
}
Remote Interface: Bank.java
package beans;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface Bank extends EJBObject {
public Account getAccount(String number) throws RemoteException;
public void transfer(Account from, Account to, double amount)
throws RemoteException, IllegalOperationException;
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 204 von total 218 Seiten
J2EE – EJB 2.x
Bean Class: BankBean.java
package beans;
import
import
import
import
import
import
import
javax.ejb.CreateException;
javax.ejb.EJBException;
javax.ejb.SessionBean;
javax.ejb.SessionContext;
javax.naming.InitialContext;
javax.naming.NamingException;
javax.rmi.PortableRemoteObject;
public class BankBean implements SessionBean {
private SessionContext context;
private AccountHome accountHome;
public void ejbCreate() throws CreateException {
log();
try {
InitialContext initial = new InitialContext();
Object objref = initial.lookup("java:comp/env/ejb/AccountHome");
accountHome = (AccountHome) PortableRemoteObject.narrow(objref,
AccountHome.class);
} catch (NamingException e) {
throw new EJBException(e.getMessage());
}
}
public void ejbRemove() {
log();
}
public void ejbActivate() {
log();
}
public void ejbPassivate() {
log();
}
public void setSessionContext(SessionContext context) {
log();
this.context = context;
}
public Account getAccount(String number) {
log();
try {
return accountHome.findByPrimaryKey(number);
} catch (Exception e) {
throw new EJBException(e.getMessage());
}
}
public void transfer(Account from, Account to, double amount)
throws IllegalOperationException {
log();
try {
to.deposit(amount);
from.withdraw(amount);
} catch (IllegalOperationException e) {
context.setRollbackOnly();
} catch (Exception e) {
throw new EJBException(e.getMessage());
}
}
private void log() {
StackTraceElement ste = new Exception().getStackTrace()[1];
System.out.println(this + "[" + Thread.currentThread().hashCode() + "]: "
+ ste.getMethodName());
}
}
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 205 von total 218 Seiten
J2EE – EJB 2.x
Exception Class: IllegalOperationException.java
package beans;
public class IllegalOperationException extends Exception {
public IllegalOperationException(String reason) {
System.out.println(reason);
}
}
Deployment Descriptor: ejbejb-jar.xml.java
<ejb-jar>
<enterprise-beans>
<entity>
<display-name>AccountEJB</display-name>
<ejb-name>AccountEJB</ejb-name>
<home>beans.AccountHome</home>
<remote>beans.Account</remote>
<ejb-class>beans.AccountBean</ejb-class>
<persistence-type>Bean</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>False</reentrant>
<resource-ref>
<res-ref-name>jdbc/BankDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
</entity>
<session>
<display-name>BankEJB</display-name>
<ejb-name>BankEJB</ejb-name>
<home>beans.BankHome</home>
<remote>beans.Bank</remote>
<ejb-class>beans.BankBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<ejb-ref>
<ejb-ref-name>ejb/AccountHome</ejb-ref-name>
<ejb-ref-type>Entity</ejb-ref-type>
<home>beans.AccountHome</home>
<remote>beans.Account</remote>
<ejb-link>AccountEJB</ejb-link>
</ejb-ref>
</session>
</enterprise-beans>
<assembly-descriptor>
<!-- BankEJB -->
<container-transaction>
<method>
<ejb-name>BankEJB</ejb-name>
<method-name>transfer</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<!-- AccountEJB -->
<container-transaction>
<method>
<ejb-name>AccountEJB</ejb-name>
<method-name>deposit</method-name>
</method>
<method>
<ejb-name>AccountEJB</ejb-name>
<method-name>withdraw</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
<container-transaction>
<method>
<ejb-name>AccountEJB</ejb-name>
<method-name>getBalance</method-name>
</method>
<method>
<ejb-name>AccountEJB</ejb-name>
<method-name>getOwner</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 206 von total 218 Seiten
J2EE – EJB 2.x
29.15
Lösung zu Übung ee11 : MDB - WetterTopic
Bean Class: WetterBean.java
package beans;
import javax.ejb.MessageDrivenContext;
import javax.jms.MapMessage;
public class WetterBean implements javax.ejb.MessageDrivenBean,
javax.jms.MessageListener {
private MessageDrivenContext ejbContext;
public void onMessage(javax.jms.Message message) {
try {
MapMessage mapMessage = (MapMessage) message;
System.out.println("WetterMDB: Wetter=" + mapMessage.getString("Wetterlage")
+ " Wind=" + mapMessage.getString("Windrichtung") + " Temp="
+ mapMessage.getDouble("Temperatur") + "C");
} catch (Exception ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
public void ejbCreate() {
}
public void ejbRemove() {
ejbContext = null;
}
public void setMessageDrivenContext(MessageDrivenContext messageDrivenContext) {
this.ejbContext = messageDrivenContext;
}
}
Client Application: WetterClient.java
package client;
import javax.naming.*;
import java.util.Hashtable;
import javax.jms.*;
public class WetterClient {
public static void main(String[] args) throws Exception {
Hashtable props = new Hashtable();
props.put("java.naming.factory.initial",
"org.jnp.interfaces.NamingContextFactory");
props.put("java.naming.provider.url", "jnp://localhost:1099");
props.put("java.naming.factory.url.pkgs",
"org.jboss.naming:org.jnp.interfaces");
props.put("jnp.socket.Factory", "org.jnp.interfaces.TimedSocketFactory");
Context ctx = new InitialContext(props);
TopicConnectionFactory qcf = (TopicConnectionFactory) ctx
.lookup("ConnectionFactory");
Topic topic = (Topic) ctx.lookup("topic/WetterTopic");
TopicConnection connect = qcf.createTopicConnection();
TopicSession session = connect.createTopicSession(false,
TopicSession.AUTO_ACKNOWLEDGE);
connect.start();
TopicPublisher publisher = session.createPublisher(topic);
String[] wetterlagen = { "Sonnig", "Wolkig", "Regen" };
String[] windrichtungen = { "Nord", "West", "Sued", "Ost" };
for (int i = 0; i < 10; i++) {
int rndm = (int) (Math.random() * 300);
String sWetterlage = wetterlagen[rndm % 3];
String sWindrichtung = windrichtungen[rndm % 4];
double dTemperatur = (double) (rndm - 50) / 10;
MapMessage msg = session.createMapMessage();
msg.setString("Wetterlage", sWetterlage);
msg.setString("Windrichtung", sWindrichtung);
msg.setDouble("Temperatur", dTemperatur);
publisher.publish(msg);
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 207 von total 218 Seiten
J2EE – EJB 2.x
System.out.println("Sending Wetterlage=" + sWetterlage + " Windrichtung="
+ sWindrichtung + " Temparatur=" + dTemperatur + "C");
//
System.out.println( "Sending " + msg.toString() );
}
session.close();
connect.close();
}
}
Deployment Descriptor: ejbejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar
PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"
"http://java.sun.com/dtd/ejb-jar_2_0.dtd">
<ejb-jar>
<enterprise-beans>
<message-driven>
<ejb-name>WetterBean</ejb-name>
<ejb-class>beans.WetterBean</ejb-class>
<transaction-type>Container</transaction-type>
<acknowledge-mode>Auto-acknowledge</acknowledge-mode>
<message-driven-destination>
<destination-type>javax.jms.Topic</destination-type>
<subscription-durability>NonDurable</subscription-durability>
</message-driven-destination>
</message-driven>
</enterprise-beans>
</ejb-jar>
Deployment Descriptor: jboss.xml
<?xml version="1.0"?>
<jboss>
<enterprise-beans>
<message-driven>
<ejb-name>WetterBean</ejb-name>
<configuration-name>Standard Message Driven Bean</configuration-name>
<destination-jndi-name>topic/WetterTopic</destination-jndi-name>
</message-driven>
</enterprise-beans>
</jboss>
Output:
Sending
Sending
Sending
Sending
Sending
Sending
Sending
Sending
Sending
Sending
Wetterlage=Wolkig Windrichtung=Ost Temparatur=24.5C
Wetterlage=Regen Windrichtung=Sued Temparatur=15.6C
Wetterlage=Regen Windrichtung=West Temparatur=0.3C
Wetterlage=Wolkig Windrichtung=Sued Temparatur=24.8C
Wetterlage=Regen Windrichtung=Sued Temparatur=14.4C
Wetterlage=Wolkig Windrichtung=West Temparatur=2.3C
Wetterlage=Sonnig Windrichtung=Sued Temparatur=16.0C
Wetterlage=Regen Windrichtung=Sued Temparatur=1.2C
Wetterlage=Sonnig Windrichtung=Sued Temparatur=2.8C
Wetterlage=Wolkig Windrichtung=West Temparatur=4.7C
ServerLog:
11:48:36,375
11:48:36,406
11:48:36,406
11:48:36,406
11:48:36,421
11:48:36,421
11:48:36,421
11:48:36,421
11:48:36,437
11:48:36,437
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
INFO
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
[STDOUT]
WetterMDB:
WetterMDB:
WetterMDB:
WetterMDB:
WetterMDB:
WetterMDB:
WetterMDB:
WetterMDB:
WetterMDB:
WetterMDB:
Wetter=Wolkig Wind=Ost Temp=24.5C
Wetter=Regen Wind=Sued Temp=15.6C
Wetter=Regen Wind=West Temp=0.3C
Wetter=Wolkig Wind=Sued Temp=24.8C
Wetter=Regen Wind=Sued Temp=14.4C
Wetter=Wolkig Wind=West Temp=2.3C
Wetter=Sonnig Wind=Sued Temp=16.0C
Wetter=Regen Wind=Sued Temp=1.2C
Wetter=Sonnig Wind=Sued Temp=2.8C
Wetter=Wolkig Wind=West Temp=4.7C
Version 3.0
Seite 208 von total 218 Seiten
J2EE – EJB 2.x
G
Glossar
Object
Annotation
Version Beschreibung
EJB 3.0
Die Programmiersprache Java stellt mit Javadoc und den Annotationen zwei Mechanismen zum
Einbinden von Metadaten zur Verfügung. Diese werden insbesondere im J2EE-Umfeld eingesetzt,
um verschiedene Dateien automatisiert zu erzeugen. Dazu zählen beispielsweise SQL-Dateien,
Deployment-Deskriptoren und die mit Enterprise Java Beans verbundenen Schnittstellen (Remote
Interface, Home Interface, ...).
Mit der Sprachversion Java 5 wurden Annotationen als ein eigenes Sprachelement geschaffen.
Annotationen werden im Quelltext durch ein @-Zeichen gefolgt vom Namen der Annotation
gekennzeichnet. Zusätzlich ist es auch möglich, Annotationen Parameter zu übergeben.
Ausserdem können den vorgegebenen Annotationen auch eigene hinzugefügt werden.
Annotationen können mittels des Annotation Processing Toolkits (APT) direkt im Quelltext oder
mittels Reflexion zur Laufzeit eines Programms ausgewertet werden. Dadurch entstehen unter
anderem weitere Einsatzmöglichkeiten im Vergleich zu Javadoc. So wertet beispielsweise der
Compiler selbst einige Annotationen aus. Das folgende Beispiel zeigt, wie man eine CompilerWarnung mittels der entsprechenden Annotation unterdrückt:
public class A {
@SuppressWarnings("unchecked")
public void meineMethode() {}
}
Attached Object
EJB 3.0
Bezeichnet eine vom Entity Manager aktuelle verwaltete Instanz einer Entity Bean mit den darin
enthaltenen Daten aus einer Datenbank.
Bean-Klasse
EJB 1.x
EJB 2.x
EJB 3.0
Die Bean-Klasse ist das Software-Artefakt einer EJB-Komponente, in der die eigentliche
Implementierung der Geschäftslogik vorhanden ist. In der gängigen OO-Schreibweise würde man
diese Klasse als Implementations-Klasse bezeichnen (z.B. KundeImpl). Im EJB-Bereich wird der
Begriff Bean-Klasse verwendet (z.B. KundeBean).
Einer der beiden Persistenzmechanismen für Entity Beans vor EJB 3.0
Bean-Managed Persistence
EJB 2.x
EJB 3.0
Bei den Entity Beans, die Bean-Managed Persistence (BMP) verwenden, sind Sie als Entwickler
dafür verantwortlich, diese Aufgabe durch Implementierung des entsprechenden Java-Codes zu
lösen. Das bedeutet im Wesentlichen, dass der Entwickler die persistenten Attribute der Entity
Bean in geeigneter Weise der Persistenzschicht (z.B. den Feldern einer Datenbanktabelle)
zuordnen muss. Hierzu implementiert er die Logik für den Zugriff auf die Persistenzschicht (z.B.
Datenbank per JDBC oder Legacy-System wie SAP per JCA ) in der Bean-Klasse.
Der EJB-Container entscheidet, wann etwas passiert; der Entwickler entscheidet, was passiert
BMP
EJB 2.x
EJB 3.0
Akronym für Bean-managed Persistence
Business-Logik
EJB 1.x
EJB 2.x
EJB 3.0
Anwendungslogik, die den Teil des Source-Codes einer Anwendung oder einer EJB bezeichnet,
welche die Funktionen bereitstellt, für die die Anwendung (Enterprise Bean) entwickelt wurde.
Business Interface
EJB 3.0
Business Interface bezeichnet in EJB 3 POJI-Typ Interfaces. D.h. diese Interfaces dürfen nicht
mehr von EJB[Local]Object erben wie dies bei den so genannten Component Interfaces der Fall
war.
Session Beans erfordern ein Business Interface. Im Gegensatz zu Entity Beans müssen EJB 3
Entitys kein Business Interface mehr aufweisen.
Bei EJB 2.x wird bei der Implementierung der Bean-Klasse einer EJB-Komponente verlangt, dass
eben diese Klassen entsprechend dem Typ der EJB-Komponente das entsprechende Callback
Interface (javax.ejb.SessionBean, javax.ejb.MessageDrivenBean oder javax.ejb.EntityBean)
implementiert. Zum einen kennzeichnen diese Interfaces, um was für einen Typ einer EJB es sich
handelt. Zum anderen definieren diese Interfaces Callback Methoden, die vom EJB-Container im
Lebenszyklus einer EJB-Komponente aufgerufen werden. Der Entwickler ist verpflichtet, die im
jeweiligen Interface deklarierten Callback-Methoden in seiner Bean-Klasse zu implementieren.
Callback-Methoden
EJB 2.x
EJB 3.0
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Bei EJB 3.0 müssen die zuvor genannten Callback Interfaces nicht mehr von der Bean-Klasse
implementiert werden. Da man nun nicht mehr gezwungen ist, die entsprechenden CallbackMethoden zu implementieren, führt dies zu einer Reduzierung von Entwicklungszeiten und aufwänden. Um es zu ermöglichen, sich in den Ablauf der Steuerung des Lebenszyklus einer
EJB-Komponente einzuklinken, gibt es ein Set von vordefinierten Meta-Annotationen. Diese
erlauben, beliebige Methoden in der Bean-Klasse zu kennzeichnen, sodass diese in Abhängigkeit
der verwendeten Meta-Annotation zu definierten Zeitpunkten aufgerufen werden. Angewendet
werden können diese Meta-Annotationen auf alle EJB-Typen. Für das Einklinken in die Steuerung
des Lebenszyklus von Session Beans und Message-driven Beans stehen die Meta-Annotationen
Version 3.0
Seite 209 von total 218 Seiten
J2EE – EJB 2.x
Object
Version Beschreibung
@PostConstruct, @PreDestroy, @PostActivate und @PrePassivate zur Verfügung. Für das
Einklinken in die Steuerung des Lebenszyklus von Entity Beans stehen folgende MetaAnnotationen zur Verfügung: @PrePersist, @PostPersist, @PreRemove, @PostRemove,
@PreUpdate, @PostUpate, @PostLoad.
CMP
EJB 2.x
EJB 3.0
Akronym für Container-managed Persistence
Component Interface
EJB 1.x
EJB 2.x
EJB 3.0
Das Component Interface ist die Geschäftsschnittstelle einer EJB-Komponente. Diese kann es in
zwei Ausprägungen geben: als lokale Schnittstelle (Zugriffe sind hierbei nur innerhalb des selben
Prozesses möglich --> siehe Local Interface) und/oder als remote Schnittstelle (hierbei können
die Client über Rechner- und Prozessgrenzen hinweg auf die EJB-Komponente zugreifen -->
siehe Remote Interface).
Configuration by Exception
EJB 3.0
Einer der beiden Persistenzmechanismen für Entity Beans vor EJB 3.0
EJB 2.x
Container-Managed Persistence
EJB 3.0
Bei der Verwendung von Container-Managed Persistence (CMP) brauchen Sie als Entwickler die
Logik zur Synchronisation zwischen Entity Beans und Persistenzschicht nicht zu implementieren.
Sie wird durch Werkzeuge des Applikationsservers generiert. Die Aufgabe des Entwicklers
beschränkt sich auf die Deklaration der persistenten Attribute sowie der Beziehungen zwischen
Entity Beans. Die Synchronisation zur Laufzeit wird durch einen speziellen Teil des EJBContainers - den sogenannten Persistenz-Manager - sichergestellt.
DTO
Data Transfere Object
EJB 2.x
EJB 3.0
Ab JEE Version 5 unterstützt das System ein Attachment, Detachment und Reattachment. Die
Entity Bean ist nun ein POJO, dessen Persistenz mit Hilfe des EntityManagers gesteuert werden
kann. Das bekannte JEE Design Pattern "Datentransferobjekt" (englisch DataTransferObjekt,
kurz: DTO) ist somit obsolet, da nun das Geschäftsobjekt selbst über verschiedene Schichten
beispielsweise zu einem Client transportiert werden kann. Datentransferobjekte dienten der
Kapselung von Geschäftsobjekten (also die reinen Daten, ohne Verhalten), und dessen
Entkopplung von technischem Code. Gewöhnlich ist mit JEE 5 die recht umständliche und
mühselige Nutzung von "Datentransferobjekten" ein Anti-Pattern.
DD
EJB 1.x
EJB 2.x
EJB 3.0
Akronym für Deployment Descriptor
Detached Entity
Detached Object
EJB 3.0
Bezeichnet eine Instanz einer EJB 3 Entity, die nicht mehr mit ihrem originären Persistence
Context assoziiert ist. Damit ist eine Synchronisierung ihrer Daten mit der Datenbank nicht mehr
gegeben. Ein solches Objekt wird nicht mehr durch den EntityManager verwaltet; es ist also eine
vom EntityManager nicht mehr gemanagte Instanz einer EJB 3 Entity.
Detached Objects können durch verschiedene Umstände entstehen. U.a. beispielsweise dann,
wenn eine Instanz einer EJB 3 Entity serialisiert wird. In diesem Fall ist die serialisierte Form der
EJB 3 Entity ein Detached Object.
Der Begriff Dependency Injection (DI) ist eine spezialisierte Version des Inversion of ControlMusters. Martin Fowler hat diesen Begriff in http://martinfowler.com/articles/injection.html
eingeführt, da die Charakteristik der Verwendung der Inversion of Control in den aktuell in den
Medien stark vertretenen leichtgewichtigen Container/Frameworks eher den Charakter der
Injektion von Referenzen über benötigte Objekte/Klassen hat.
Dependency Injection
EJB 3.0
Die Motivation für die Einführung von DI in EJB 3 ist es, für EJB-Komponenten den Zugriff auf
Ressourcen bzw. den Erhalt von Referenzen auf Ressourcen zu vereinfachen und damit letztlich
auch die Notwendigkeit des Implementierens von Code für den Zugriff über die JNDI API.
Dependency Injection bedeutet für die EJB-Technologie, dass der Entwickler den Code für den
Erhalt von Referenzen auf Ressourcen mittels der Nutzung der JNDI API nicht mehr selbst
implementieren muss, sondern die benötigten Referenzen durch die Verwendung von
Annotationen bzw. die Hinterlegung in XML anfordert. Der Container stellt dann zur Laufzeit die
benötigten Referenzen auf Ressourcen zur Verfügung. D.h. EJB-Komponenten fragen nicht mehr
aktiv den sie umgebenden Container nach Referenzen, sondern bekommen Referenzen auf
Ressourcen von außen - vom Container - injiziert.
Der Entwickler annotiert dazu mittels Meta-Annotation wahlweise Instanzvariablen oder SetterMethoden einer EJB-Komponente, wo der EJB-Container die gewünschten Referenzen injizieren
(einfügen) soll. Optional kann der Entwickler diese Informationen auch in XML hinterlegen. Der
Container sorgt automatisch dafür, dass spätestens vor der Ausführung der ersten Methode der
EJB-Komponente die entsprechenden Referenzen der EJB-Komponente zur Verfügung gestellt
werden.
Die für EJB 3 bevorzugte Variante ist die Nuztung von Annotationen.
Deployment
EJB 1.x
EJB 2.x
EJB 3.0
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Prozess der Integration einer Komponente in den J2EE-Server.
Version 3.0
Seite 210 von total 218 Seiten
J2EE – EJB 2.x
Object
Deployment Descriptor
Version Beschreibung
EJB 1.x
EJB 2.x
EJB 3.0
Der Deployment Deskriptor enthält im XML-Format Laufzeit- und Kompilierungszeitinformationen
für die Generierung und Ausführung von EJB-Komponenten. Hierbei handelt es sich um
deklarative Programmierung, da der Entwickler Features/Funktionalitäten der EJB-Komponenten
durch "einfaches" angeben bestimmter Schlüsselwörter innerhalb eines Deployment Deskriptors
erreichen kann, ohne programmieren zu müssen. Beispielsweise lässt sich darüber die
Transaktionalität von EJB-Komponenten steuern.
Bezeichnet eine Fetching-Strategie der Java Persistence API. Einfach gesagt bedeutet Eager
Load, dass die Daten einer EJB 3 Entity sofort vollständig aus der Datenbank gelesen und in die
entsprechenden Attribute einer Instanz geschrieben werden. Alle anderen innerhalb der
Instanz/des Objektes referenzierten persistenten Objekte (Objektnetze/Objektbäume) werden
ebenfalls sofort instanziiert und vollständig geladen.
Eager Load
EJB 3.0
Bei größeren Objektnetzen oder Objektbäumen kann dieser Ansatz in Verbindung mit einer großen
Datenmenge schnell zu langen Ladezeiten und hohem Speicherverbrauch durch eine
entsprechende Anzahl an mit Daten gefüllten Objekten führen.
Die Eager Load Fetching-Strategie wird, wenn durch den Entwickler keine Fetching-Strategie
angegeben oder als Fetching-Strategie explizit Eager Load angegeben worden ist, von der
Persistence Provider Runtime angewendet.
EJB
EJB 1.x
EJB 2.x
EJB 3.0
Akronym für Enterprise JavaBeans, der Bezeichnung des serverseitigen Komponentenmodells der
Java EE-Plattform.
EJB 3.0
In EJB 3 wird der EJB-Typ zur Abbildung und Umsetzung von persistenten Entitäten als EJB 3
Entity und manchmal auch einfach als Persistent Entity bezeichnet. Letzteres ist dem Ansatz
geschuldet, dass die Persistenz nicht mehr nur für Java EE, sondern auch für Java SE nutzbar
sein soll.
EJB 1.x
EJB 2.x
EJB 3.0
Generelle Bezeichnung der Komponenten.
EJB 3 Entity
EJB-Komponente
EJB-Typen
EJB stellt ab EJB 2.x drei EJB-Typen zur Verfügung:
EJB 1.x
EJB 2.x
EJB 3.0
Session Bean (in den zwei Ausprägungen Stateful und Stateless)
Message-driven Bean
Entity Bean
Ab EJB 3 gibt es die EJB-Typen oder Ausprägungen:
-
Session Bean (in den zwei Ausprägungen Stateful und Stateless)
Message-driven Bean
EJB 3 Entity oder Persistent Entity
Anzumerken ist, dass Session Bean und Message-driven Bean mit EJB 3 genereller gesprochen
als Enterprise Beans bezeichnet werden und die EJB 2.x Enity Beans nun als EJB 3 Entity oder
auch als Persitent Entity bezeichnet werden.
EJB QL
EJB 1.x
EJB 2.x
Akronym für EJB Query Language. Beschreibung siehe EJB Query Language
Entity Manager
EJB 3.0
Neues Element in der Java EE bzw. Java SE, welches mit EJB 3.0 eingeführt worden ist. Hierbei
handelt es sich (analog zu Ansätzen aus JDO und Hibernate) um den zentralen
Persistenzmanager. Der Entity Manager ist dafür zuständig, die gesamte Persistenzabbildung von
Entitäten (POJOs) zur Verfügung zu stellen. Der Entity Manager kümmert sich um das persistente
Neuanlegen von Entitäten in Datenbanken, das Laden, Speichern, Löschen, Suchen, und sorgt
für die konsistenten Datenzustände bei konkurrierenden Zugriffen (Concurrency Handling) auf
Entitäten.
EJB-Spezifikation
EJB 1.x
EJB 2.x
EJB 3.0
Beschreibung der Regeln, nach denen die Schnittstellen der einzelnen Komponenten bezüglich
der EJB-Technologie zu entwerfen sind, damit die Modularität des Systems stets gewährleistet ist.
Komponente, die den Enterprise JavaBeans eine Laufzeitumgebung zur Verfügung stellt. Die
Beans laufen innerhalb eines Containers. Der EJB-Kontainer selbst ist Bestandteil des J2EE
Servers und hat folgende Aufgaben:
EJB-Kontainer
EJB 1.x
EJB 2.x
EJB 3.0
Lebenszyklus-Management
Das Objekt einer EJB-Klasse kann sich in unterschiedlichen Zuständen (Lebenszyklen) befinden.
Der Container kann eine durch einen Clientaufruf instanzierte EJB passivieren und zeitweise aus
dem Arbeitsspeicher entfernen, ohne es komplett zu verwerfen (löschen).
Sicherheit
überwacht sämtliche Zugriffe auf die Methoden von EJBs. Falls im Deploymentdescriptor einer
Bean entsprechende, sicherheitsrelevante Angaben gemacht werden, erzwingt der Container vom
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 211 von total 218 Seiten
J2EE – EJB 2.x
Object
Version Beschreibung
Client die Autorisierung vor der Ausführung der aufgerufenen Methode.
Transaktionsmanagement
Clients können die Methoden von EJBs nicht ohne die Kontrolle des Containers aufrufen. Durch
die Angaben im Deployment Descriptor einer EJB kennt der Container die transaktionalen
Erfordernisse für jede Methode der Beans.
Verbindungsmanagement für Clients ( remote client connectivity )
Clients können die Methoden von EJBs nicht direkt aufrufen. Sie bekommen vom Container ein
Remote-Object, dessen Methoden-Signatur identisch mit der Signatur der Bean-Methoden ist.
Die Methodenaufrufe am Remote-Object werden unter Kontrolle des Containers an die BeanMethoden weitergeleitet. Ausserdem importiert der Client die Stub-Klasse für das Remote Objekt.
Dem Client erscheint somit das Remote-Objekt als würde es sich im gleichen Adressraum (VM)
befinden.
Verbindungsmanagement für Datenbanken (database connection pooling)
Das erstmalige Herstellen einer Datenbankverbindung ist, verglichen mit anderen Operationen,
immer ein sehr zeitintensiver (teurer) Vorgang. Der Container übernimmt diese Aufgabe und
sammelt die Verbindungen in einem Pool. Über diesen Pool stellt er die Datenbankverbindungen
den EJBs zur Verfügung.
Enterprise-Anwendung
EJB 1.x
EJB 2.x
EJB 3.0
beinhaltet EnterpriseBeans wie auch Bestandteile einer WEB-Anwendung (HTML-Dateien,
Servlets, JSPs) und kann über einen J2EE-Server verteilt sein.
Enterprise JavaBean
EJB 1.x
EJB 2.x
EJB 3.0
Komponente, welche innerhalb eines EJB-Kontainers ausgeführt wird und dabei die BusinessLogik enthält oder die Daten einer Datenbank repräsentiert.
Das Home Interface einer EJB-Komponente enthält alle Methoden zur Steuerung des Lebenszyklus der EJB-Komponente. Da Clients rein technisch keine Möglichkeiten besitzen,
Instanzen von EJB-Komponenten über Rechner- und/oder Prozessgrenzen hinweg erzeugen,
löschen oder laden zu können, müssen sie sich dazu an das jeweilige Home Interface der
gewünschten EJB-Komponente wenden. Dieses stellt die entsprechenden Funktionalitäten zur
Verfügung. Durch die Verwendung vom Home Interface wird eine Entkopplung von Clients und
EJB-Komponenten erreicht. Das Home Interface entspricht dem Entwurfsmuster der "Factory
Method".
Konkret gibt es von einem Home Interface zwei Varianten:
Remote Home Interface
für entfernte Zugriffe über Rechner- und/oder Prozessgrenzen hinweg
Home Interface
EJB 1.x
EJB 2.x
Local Home Interface
für lokale Kommunikation durch In-Prozess-Methodenaufrufe
Mit EJB 3.0 ist es möglich - analog CORBA-basierten Systemen, in denen InterceptorMechanismen seit langem bekannt sind und eingesetzt werden - Methodenaufrufe von
Geschäftsmethoden abzufangen, die darin enthaltenen Parameterwerte zu untersuchen, bei
Bedarf zu manipulieren und die Aufrufe weiterzuleiten oder bei Bedarf abzubrechen bzw. zu
verhindern.
Interceptor
EJB 3.0
Mit dem Interceptor-Mechanismus hält die aspektorientierte Programmierung - zumindest in
Ansätzen - Einzug in die EJB-basierte Entwicklung. Interceptors ermöglichen das transparente
Einflechten von Funktionalitäten (z.B. Logging, Tracing, Zeitmessung, Sicherheitsprüfungen), die
fachliche Entwickler von technischen Aspekten entkoppelt.
Der Entwickler eines Interceptors kann für eine abgefangene Methode innerhalb des aktivierten
Interceptors beliebigen von ihm hinterlegten Code ausführen zu lassen.
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 212 von total 218 Seiten
J2EE – EJB 2.x
Object
Version Beschreibung
Inversion of Control (IoC) ist eine allgemeine Eigenschaft von Frameworks, wird gemeinhin gerne
mit dem Satz "don't call us, we call you" umschrieben und soll ausdrücken, dass die
Verantwortlichkeiten bei der Programmausführung beim Framework liegen und nicht bei den
Komponenten, die auf Basis eines Frameworks entwickelt und ausgeführt werden.
Inversion of Control
EJB 3.0
D.h. das die Komponenten umgebende Framework verlangt von den Komponenten, dass
bestimmte Callback-Methoden durch sie implementiert werden, über die das Framework bzw. der
Container zur Laufzeit Informationen in die Komponenten injiziert ober bestimmtes Verhalten (über
Methodenaufrufe) auslöst.
Im Kontext von EJB 3.0 wird IoC und Dependency Injection vielfach synonym verwendet, auch
wenn es im Detail sehr wohl Unterschiede gibt (siehe hierzu auch Dependency Injection). Das,
was bei EJB 3.0 zum Einsatz kommt, ist eine spezialisierte Form von Inversion of Control und wird
als Dependency Injection bezeichnet.
Die lokale Variante des Home Interfaces ist das Local Home Interface. Es ist sozusagen die
"Hochgeschwindigkeits-Variante" des Home Interfaces. Das Local Home Interface kommt
ausschließlich dann zum Einsatz, wenn sich Client und zu rufende EJB-Komponente im selben
EJB-Container (bzw. Prozess) befinden. Analog dem Remote Home Interface dient auch das
Local Home Interface dazu, EJB-Komponenten erzeugen, löschen und ggf. laden zu lassen.
Local Home Interface
EJB 2.x
Das Local Home Interface ist "schlanker" als das Remote Home Interface, da es nicht über RMI
angesprochen werden kann und keine Funktionalitäten für das Packen, Entpacken und
Transferieren von Methodenaufrufen zum Transport über das Netzwerk zur Verfügung stellen
muss. Die Kommunikation zwischen Client und EJB-Komponente erfolgt mittels In-ProzessMethodenaufrufen und ist somit schneller als unter Verwendung eines Remote Home Interfaces.
Das Local Home Interface ist nicht in der Lage, entfernte Methodenaufrufe zu empfangen.
<insert Grafik Local Home Interface>
Local Interface
EJB 2.x
Die technische Ausprägung der Geschäftsschnittstelle (Component Interface) einer EJBKomponente, die ausschließlich die lokale Kommunikation - die Kommunikation zwischen Client
und EJB-Komponente erfolgt im selben Prozessraum - ermöglicht, wird als Local Interface
bezeichnet. Wenn eine EJB-Komponente nur ein Local Interface besitzt, so ist es nicht möglich,
dass ein Remote-Client auf die entfernte EJB-Komponente zugreifen kann.
Managed Object
EJB 3.0
Bezeichnet eine vom Entity Manager überwachte Entity Bean bzw. Entität. Überwacht bezieht sich
hierbei auf die Steuerung des Persistenzverhaltens der Entität.
Das Multi-table Mapping bezeichnet die Fähigkeit einer EJB 3.0 Entity Bean, auf mehrere
physikalische (normalisierte) Datenbanktabellen gemappt zu werden. Diese Fähigkeit gab es in
EJB 2.x - ohne expliziten Programmieraufwand - nicht. In EJB 3.0 ist eine Entity Bean kein
stumpfes 1:1 Mapping einer Tabelle mehr, sondern vielmehr die technische Hülle für ein logisches
Geschäftsobjekt, dessen Daten physikalisch über mehrere Tabellen verteilt sein können. Es wird
innerhalb der Entity Bean über Annotationen einfach angegeben, aus welchen Tabellen/Feldern
die entsprechenden Daten stammen. Der Entity Manager sorgt dafür, das relationale Modell zur
Laufzeit aufzulösen und die Daten in die Entity Bean zu übertragen (siehe Abbildung).
Multi-Table Mapping
EJB 3.0
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 213 von total 218 Seiten
J2EE – EJB 2.x
Object
Version Beschreibung
Logisches Geschäftsobjekt (Entity Bean), bestehend aus drei Tabellen
@Entity
@Table(name = "KUNDE")
@SecondaryTables({
@SecondaryTable(name = "ADRESSE")
@SecondaryTable(name = "TYP") })
public class Kunde implements java.io.Serializable {
private int id;
private String name; // Aus Tabelle "KUNDE"
private String vorname ; // Aus Tabelle "KUNDE"
private String strasse; // Aus Sekundärtabelle "ADRESSE"
...
// Attribut aus der Haupttabelle "Kunde"
@Column(name = "NAME")
public String getName() {
return name;
}
...
// Attribut aus der Sekundärtabelle "ADRESSE"
@Column(name = "STRASSE", secondaryTable = "ADRESSE")
public String getStrasse() {
return strasse;
}
...
// Per Default wird, wenn kein @Column angeben ist,
// davon ausgegangen, dass der Spaltenname gleich dem
// Attributnamen ist.
public String getVorname() {
return vorname ;
}
}
Der Begriff taucht verstärkt seit 2004 in Fachzeitschriften und eZines im Java-Bereich auf. Mit
Plain Old Java Interface bzw. POJI wird ein normales - nicht durch technischen oder
infrastrukturellen Code verschmutztes - Java Interface bezeichnet.
Plain Old Java Interface
EJB 3.0
Im Kontext von EJB 3.0 wird der Begriff Plain Old Java Interface bzw. POJI nun auch angewandt.
Eine Kernidee von EJB 3.0 ist, dass die EJB-Komponenten aus Sicht des Entwicklers - in ihrer
Nutzung und in ihrer Mikroarchitektur so weit vereinfacht werden, dass Component Interfaces
(also die Geschäftsschnittstellen)
Der Begriff taucht verstärkt seit 2004 in Fachzeitschriften und im Java-Bereich auf. Mit Plain Old
Java Object wird eine normale, einfache Java-Klasse (na ja, vielleicht sollte POJO dann lieber
POJC heissen ;-) bezeichnet, die nicht durch infrastrukturellen Code, technische Code etc.
"verschmutzt" ist, wie dies beispielsweise bei komplexeren Komponenten vorkommt.
Plain Old Java Object
EJB 3.0
Im Kontext von EJB 3.0 wird der Begriff Plain Old Java Object bzw. POJO nun auch angewandt.
Eine Kernidee von EJB 3.0 ist, dass die EJB-Komponenten - aus Sicht des Entwicklers - in ihrer
Nutzung und in ihrer Mikroarchitektur so weit vereinfacht werden, dass die Bean-Klassen (die
Implementierungs-Klassen der EJB-Komponenten) von ihrem Typ her - im Vergleich zu EJBVorgängerversionen - als POJOs bezeichnet werden können.
POJO
EJB 3.0
Akronym für Plain Old Java Object
POJI
EJB 3.0
Akronym für Plain Old Java Interface
Remote Home Interface
EJB 1.x
EJB 2.x
Das Remote Home Interface ermöglicht es entfernten Clients, über das Netzwerk Anfragen zum
Erzeugen, Laden und Löschen von Instanzen zu stellen. Clients kommunizieren mit einem Remote
Home Interface über entfernte Methodenaufrufe, die über das RMI/IIOP-Protokoll transferiert
werden. Diese Art der Kommunikation ist langsamer als die Kommunikation mittels In-ProzessMethodenaufrufe innerhalb eines Prozesses. Die Verwendung dieser Ausprägung des Home
Interfaces ist notwendig, wenn man Clients, die sich in einem verteilten System auf einem
beliebigen Rechner in einem Netzwerk befinden können, eine EJB-Komponente offerieren
möchte.
<insert Grafik Remote Home Interface>
Remote Interface
EJB 1.x
EJB 2.x
Die technische Ausprägung der Geschäftsschnittstelle (Component Interface) einer EJBKomponente, welche die Remote-Kommunikation (also entfernte Kommunikation) ermöglicht,
wird als Remote Interface bezeichnet.
Wenn eine EJB-Komponente ein Remote Interface besitzt, ist es Clients in anderen
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 214 von total 218 Seiten
J2EE – EJB 2.x
Object
Version Beschreibung
Prozessräumen möglich über das Netzwerk und Rechner- und Prozessgrenzen hinweg auf eben
diese EJB-Komponete zuzugreifen. Die Kommunikation erfolgt dabei über RMI/IIOP.
Selbst wenn sich Client und EJB-Komponente im selben Prozessraum befinden (z.B. ist der Client
auch eine EJB-Komponente, die eine andere EJB-Komponente verwendet), und der Client die
EJB-Komponente über ihr Remote Interface anspricht, so erfolgt die Kommunikation für
gewöhnlich über RMI/IIOP, was in diesem Fall eine unnötige Verlangsamung des Zugriffs wäre,
der durch zur Verfügungstellen einer lokalen Geschäftsschnittstelle (Local Interface) sehr einfach
behoben werden kann und zu Performanzsteigerungen führt.
© 2008 Stephan Metzler
Lösungen zu Übungen aus dem Skript
Version 3.0
Seite 215 von total 218 Seiten
J2EE – EJB 2.x
H
Referenzen
30
Quellenangaben
Die Liste der Quellenangaben stellt ein Ausschnitt der vorhandenen Literatur und Onlinequellen
dar. Bei der Beschaffung von weiterführenden Informationen ist auf die Kompatibilität zu der
verwendeten AS Version und zur EJB Technologie zu achten.
30.1
Bücher
J2EE, Einstieg für Anspruchsvolle
Thomas Stark
Addison-Wesley
ISBN 3-8273-2184-0
Core J2EE Patterns,
Patterns, Best Practices and Design Strategies
Deepak Alur, Joh Crupi, Dan Malks
Pearson Professional Education
ISBN 0-13-142246-4
J2EE Patterns, Entwurfsmuster für die J2EE
Adam Bien
Addison-Wesley
ISBN 3-8273-2124-7
Entwurfsmuster, Elemente wiederverwendbarer
wiederverwendbarer objektorientierter Software
Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
Addison-Wesley
ISBN 3-8273-1862-9
Mastering Enterprise JavaBeans
Ed Roman, Rima Patel Sriganesh, Gerald Brose
Wiley
ISBN 0-7645-7682-8
Enterprise Java Security
Marco Pistoia, Nataraj Nagaratnam, Larry Koved, Anthony Nadalin
Addison-Wesley
ISBN 9-780321-118899
© 2008 Stephan Metzler
Quellenangaben
Version 3.0
Seite 216 von total 218 Seiten
J2EE – EJB 2.x
J2EE Security
Pankaj Kumar
Addison-Wesley
ISBN 0-13-140264-1
30.2
Online Quellen
SUN
The J2EE™ 1.4 Tutorial
For Sun Java System Application Server Platform Edition 8.1 2005Q2 UR2
http://java.sun.com/j2ee/1.4/docs/tutorial/doc/index.html
The J2EE™ 1.4 Tutorial is a guide to developing enterprise applications for the Java 2
Platform, Enterprise Edition (J2EE) version 1.4
weitere
http://developers.sun.com
http://www.j2ee-develop.de
http://forum.java.sun.com
http://www.galileocomputing.de/openbook/java2
http://www.unix.org.ua/orelly/java-ent
http://www.jeckle.de
http://www.ejbkomplett.net
http://www.theserverside.com/tt/books/wiley/masteringEJB3/index.tss
http://www-306.ibm.com/software/websphere
http://www.pc-ware.de/de/PC-Ware/ssl/oracle/appl_server.htm
Technologien
http://ch.sun.com
http://www.jboss.com
http://tomcat.apache.org
http://ant.apache.org
http://www.mysql.de
© 2008 Stephan Metzler
Quellenangaben
Version 3.0
Seite 217 von total 218 Seiten
J2EE – EJB 2.x
31
Installierte Software
Die für die Ausführung der Beispiele verwendeten Softwareversionen sind:
Java SDK
- jdk-1_5_0_07-windows-i586-p
Java EE
- j2eesdk-1_4_03-windows
Applikation Server
- j2eesdk-1_4_03-windows
- jboss-4.0.4.GA
Web Server
- apache-tomcat-5.5.17
mySQL
- mysql-noinstall-5.0.24-win32
- mysql-gui-tools-noinstall-5.0-r2-win32
- mysql-connector-java-5.0.3
Entwicklungsumgebung
- eclipse-SDK-3.2-win32
- plugin : Quantum_2.4.5_for_Eclipse_3
- plugin : xmlbuddy_2.0.72
- plugin: tomcatPluginV31
© 2008 Stephan Metzler
Installierte Software
Version 3.0
Seite 218 von total 218 Seiten
Herunterladen