Enterprise Java Beans Thomas van Husen 1 (c) Libra 2001 Standard Edition J2SE 1.3.1 1.4 Java Enterprise Edition J2EE 1.3 2 (c) Libra 2001 J2EE 1.3 packages Package App Client Applet Web EJB JDBC 2.0 Extension EJB 2.0 JA N JA JA JA N JA JA Servlets 2.3 2.3 Servlets N N JA N JSP 1.2 JSP 1.2 N N JA N JMS 1.0 JMS JA N JA JA JTA 1.0 N N JA JA JavaMail 1.2 N N JA JA JAF 1.0 N N JA JA JA N JA JA N N JA JA JA N JA JA EJB 2.0 JAXPJAXP 1.1 1.1 Connector 1.0 JAASJAAS 1.0 1.0 3 (c) Libra 2001 J2EE Architecture Applet Container Applet Webcontainer J2SE JAF J2SE Connectors JAF JAXP JDBC Java Mail Java Mail JTA JAAS JMS EJB JAXP JDBC Application Client JTA JAAS JMS Application Client Container Servlet Connectors J2SE JSP EJB Container DB JDBC JAXP JAAS JMS J2SE 4 (c) Libra 2001 Firewall J2EE Environment EJB Container Client Entity Bean Session Bean Client Client Web Container Entity Bean Enterprise Information System Database ERP Legacy Applications Servlets Client Client Tier JSP, HTML Middle Tier EIS Tier 5 (c) Libra 2001 J2EE Application Scenarios Browser Web Container Stand-Alone Client EJB Container Database 6 (c) Libra 2001 EJB Enterprise Java Beans Client A EJB Container Kunde Entity Bean Kunde Remote Object JDBC Client B Kunde Remote Object Kunde Nr ... Kunde Name Ort 5153 Weka Bau Stuttga rt ... 7 (c) Libra 2001 Presentation Tier Browser A Web Container Kunde Entity Bean Controller Session A Model Page Browser B EJB Container Session B Model Gui Client A Gui Client B JDBC Kunde Remote Object Kunde Remote Object Kunde Nr ... Kunde Name Ort 5153 Weka Bau Stuttga rt ... 8 (c) Libra 2001 Enterprise Beans EJB Container Home Product Remote Client Home Order Callback Methoden: Jede Bean implementiert einen Subtyp des EnterpriseBean Interfaces, das verschiedene Methoden definiert, die "Callbacks" genannt werden. Jede Callback Methode instruiert die Bean über einen bestimmten Event in ihrem Lebenszyklus. Der Container ruft diese Methoden auf, wenn die Bean aktiviert wird, wenn sie gespeichert werden soll, wenn sie passiviert wird usw. Die Bean kann in den Callback Methoden das Ihrige tun, um dem jeweiligen Event gerecht zu werden. Remote 9 (c) Libra 2001 Enterprise Beans Client EJB Container Home create() Remote newOrder() selectCustomer() addLine() Home findByPrimaryKey() Home Interface Order Controller Session Bean Remote Interface Implementierung Product Enitity Bean Remote getDetail() setDetail() 10 (c) Libra 2001 EJB Container EJB Container Client Transaktion Management Persistence Management Security Management Jndi ENC, EJBContext Bean Callback Methods Der Container isoliert die Enterprise Bean gegen den direkten Zugriff eines Clients. Wenn eine Client Anwendung eine "remote" Methode einer Bean aufruft, fängt der Container diesen Aufruf ab und sichert die Belange von Persistenz, Transaktionsmanagement und Security. Security, Transaktionssteuerung und Persistenz werden vom Container automatisch gesteuert, ohne dass sich der Client darum kümmern muss. Callback Methoden: Jede Bean implementiert einen Subtyp des EnterpriseBean Interfaces, das verschiedene Methoden definiert, die "Callbacks" genannt werden. Jede Callback Methode instruiert die Bean über einen bestimmten Event in ihrem Lebenszyklus. Der Container ruft diese Methoden auf, wenn die Bean aktiviert wird, wenn sie gespeichert werden soll, wenn sie passiviert wird usw. Die Bean kann in den Callback Methoden das Ihrige tun, um dem jeweiligen Event gerecht zu werden. 11 (c) Libra 2001 Entity und Session Beans EJB Container • Entity Bean Customer Order Product zwei grundsätzliche Typen von Enterprise Beans: Entity Beans und Session Beans. Entity Beans stehen für Objekte, die persistent in einer Datenbank gehalten werden, und können typischerweise mit einem Substantiv bezeichnet werden. Session Beans repräsentieren hingegen Prozesse und Aufgaben. Kandidaten für Session Beans sind Objekte, die mit Verben bezeichnet werden. . • Session Bean Order Controller 12 (c) Libra 2001 Session – Entity Beans Browser A Web Container Auftrag Remote Auftrag Remote EJB Container Kunde Entity Bean Auftrag Session Bean A Kunde Remote Browser B Kunde Remote Auftrag Session Bean B Gui Client C Kunde Remote Object Auftrag Remote Object Gui Client D Kunde Remote Object Auftrag Remote Object Auftrag Session Bean C Auftrag Session Bean D 13 (c) Libra 2001 Entity Bean Product Bean Home Interface create(5153, Cotes du Rhone, 7.80) int id String name; double price; ejbCreate(...) Product ID ... Product Name Preis 5153 Cotes du Rhone 7.80 ... Remote Interface setPrice(. . .) getPrice() setPrice(double d) { price = d; } getPrice() { return price; } 14 (c) Libra 2001 Entity Bean ejbCreate der create() Methode auf dem Home Interface entspricht immer eine ejbCreate() Methode in der Bean die ejbCreate() Methode der Bean enthält (bei BMP) den SQL Code (oder DAO Aufruf), um den Record, der die Bean persistent repräsentiert, der Datenbank hinzuzufügen durch das create() auf dem Home Interface erhält der Client ein Remote Interface, das die einzelne erzeugte Bean repräsentiert und durch das der Client Methoden der Bean aufrufen kann das Remote Interface enthält keine Methode „Save“ 15 (c) Libra 2001 Product Table MEASUREUNIT MEASUREUNITID CHAR(3) MEASUREUNITDESC CHAR(50) PRODUCT PRODUCTNUM NUMERIC(7) PRODUCTDESC CHAR(50)MEASUREUNITID = MEASUREUNITID PRICE DECIMAL(15,2) MEASUREUNITID CHAR(3) create table PRODUCT ( PRODUCTNUM NUMERIC(7) PRODUCTDESC CHAR(50) PRICE DECIMAL(15,2) MEASUREUNITID CHAR(3) primary key (PRODUCTNUM) ); not null , , , , 16 (c) Libra 2001 Product Bean Impl public class ProductEJBBean implements EntityBean { private int id; private String desc; private double price; private String measureUnitId; public EntityContext context; public ProductPK ejbCreate (String desc, double price, String measureUnitId) throws CreateException, DuplicateKeyException { . . . } . . . } 17 (c) Libra 2001 Product ejbCreate() - 1 public ProductPK ejbCreate (String desc, double price, String measureUnitId) throws CreateException, DuplicateKeyException { this.desc = desc; this.price = price; this.measureUnitId = measureUnitId; String sql = "insert into PRODUCT " + "(" + "PRODUCTNUM, PRODUCTDESC, PRICE, MEASUREUNITID" + ") " + "values " + "(" + "?, ?, ?, ? " + ")"; Connection dbConnection = null; PreparedStatement stmt = null; try { try { . . . (c) Libra 2001 18 . . . Product ejbCreate() - 2 try { try { dbConnection = getDbConnection(); this.id = getMaxProductNumber(dbConnection) + 1; stmt = dbConnection.prepareStatement(sql); int i = 1; stmt.setInt(i++, this.id); stmt.setString(i++, this.desc); stmt.setDouble(i++, this.price); stmt.setString(i++, this.measureUnitId); if (stmt.executeUpdate() == 1) return new ProductPK(this.id); else throw new CreateException("Error creating Product"); } finally { if (stmt != null) stmt.close(); if (dbConnection != null) dbConnection.close(); } } catch (java.sql.SQLException e) { throw new EJBException (e); } (c) } Libra 2001 19 Entity Bean ejbCreate() Aufgaben in der ejbCreate() Methode müssen: die Instanzvariablen der Bean gesetzt werden das Objekt der Datenbank (per JDBC) hinzugefügt werden die Werte, die zum Erstellen des Objekts notwendig sind, erhält die ejbCreate() Methode als Parameter bei Erfolg gibt ejbCreate() den Primary Key zurück (auf dem Home Interface gibt die entsprechende create() Methode das Remote Object zurück) auftretende SQLExceptions (sind Subsystem Exceptions) werden in EJBExceptions (sind RuntimeExceptions) umgewandelt und geworfen 20 (c) Libra 2001 public class ProductPK implements java.io.Serializable { private final int id; public ProductPK(int id) { this.id = id; Product Primary Key } public int getId() { return id; } public boolean equals(Object o) { if (this == o) return true; if ( !(o instanceof ProductPK)) // will also return false when o == null return false; ProductPK other = (ProductPK) o; return this.id == other.id; } public int hashCode() { return id; } public String toString() { return "[" + getClass().getName() + ": " + id + "]"; } } (c) Libra 2001 21 Entity Bean Primary Key Aufgaben jede Entity Bean hat einen Primary Key im genannten Beispiel wäre es nicht notwendig gewesen, eine eigene Klasse als Primary Key zu verwenden – in der Praxis sind Primary Keys wegen der Zusammensetzung aus mehreren DB Feldern (Mandant etc.) jedoch immer eigene Klassen die PK Klasse muss das Interface java.io.Serializable haben die equals() Methode von java.lang.Object muss überschrieben werden und damit auch die hashCode() Methode die Klasse sollte als Immutable codiert werden (alle Instanzvariable final) 22 (c) Libra 2001 Primary Key im Deployment Descriptor <ejb-jar> <enterprise-beans> <entity> <description>product</description> <ejb-name>ProductEJB</ejb-name> <home>schulung.app.product.ProductEJBHome</home> <remote>schulung.app.product.ProductEJB</remote> <ejb-class>schulung.app.product.ProductEJBBean</ejb-class> <persistence-type>Bean</persistence-type> <prim-key-class>schulung.app.product.ProductPK</prim-key-class> <reentrant>False</reentrant> . . . </entity> die Primary Key Klasse wird im Deployment Descriptor der Bean eingetragen 23 (c) Libra 2001 private java.sql.Connection getDbConnection() Database Connection { InitialContext jndiContext; javax.sql.DataSource ds; try { jndiContext = new InitialContext(); ds = (javax.sql.DataSource) jndiContext.lookup("java:comp/env/jdbc/edu"); java.sql.Connection con = ds.getConnection("sa", ""); return con; } catch (javax.naming.NamingException e) { e.printStackTrace(); throw new javax.ejb.EJBException(e); } catch (java.sql.SQLException e) { e.printStackTrace(); throw new javax.ejb.EJBException(e); } } 24 (c) Libra 2001 Database Connection Aufgaben Die JDBC Connection wird prinzipiell aus einer DataSource genommen. Die DataSource wird über JNDI Lookup aufgefunden. Dies ist möglich, weil der EJB Container die DataSource in seinem JNDI Service zuvor registriert hat – entsprechend seiner (Container spezifischen Konfigurations) Einstellungen und der (J2EE Standard) Angaben im Deployment Descriptor Dadurch dass die Connection nicht direkt mit dem JDBC Driver erstellt, sondern aus einer vom Container zur Verfügung gestellten Datasource gezogen wird, kann der Container seine Services wie „Connection Pooling“ und Transaktionssteuerung „injizieren“. Bei Connection Pooling müssen im Anwendungscode Connections immer gleich wieder geschlossen werden. 25 (c) Libra 2001 Database Connection im Deployment Descriptor <ejb-jar> <enterprise-beans> <entity> <description>product</description> <ejb-name>ProductEJB</ejb-name> <home>schulung.app.product.ProductEJBHome</home> <remote>schulung.app.product.ProductEJB</remote> <ejb-class>schulung.app.product.ProductEJBBean</ejb-class> <persistence-type>Bean</persistence-type> <prim-key-class>schulung.app.product.ProductPK</prim-key-class> <reentrant>False</reentrant> <resource-ref> <description>Datasource for Product EJB</description> <res-ref-name>jdbc/edu</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> </entity> try { jndiContext = new InitialContext(); ds = (javax.sql.DataSource) jndiContext.lookup("java:comp/env/jdbc/edu"); 26 (c) Libra 2001 Home Interface Bean int id String name; double price; ejbCreate(...) Remote Interface Enterprise Bean Home, Remote, Impl Jede Enterprise Bean besteht aus drei Klassen: Home Interface, Remote Interface und Bean Implementierung Ein Client kann immer nur das Home und Remote Interface verwenden, niemals die Bean selbst Der Container verwendet die Bean Implementierungsklasse Auf dem Client wird das Home Interface verwendet, um spezifische Instanzen zu erzeugen Das Home Interface gibt ein Remote Interface zurück. Das Remote Interface ist ein Proxy für die Bean im Container. Der Client kann transparent Methoden vom Remote Interface aufrufen, als wäre es ein „lokales“ Objekt. 27 (c) Libra 2001 public static void main(String [] args) { Client - Product Insert try { Properties p = new Properties(); p.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory"); p.setProperty(Context.PROVIDER_URL, "localhost:1099"); p.setProperty("java.naming.factory.url.pkgs", container spezifische Properties "org.jboss.naming:org.jnp.interfaces"); für Initial JNDI Context InitialContext context = new InitialContext(p); Object ref = context.lookup("ProductEJB"); ProductEJBHome productEJBHome = Name für Bean aus Deployment Descriptor (ProductEJBHome) PortableRemoteObject.narrow(ref, Home Interface ProductEJBHome.class); // insert ProductEJB productCrt = productEJBHome.create("Cotes du Rhone", 7.8, "fl"); System.out.println("Product: id=" + productCrt.getId() Remote Interface + ", desc=" + productCrt.getDesc() + ", price=" + productCrt.getPrice() + ", measureUnitPK=" + productCrt.getMeasureUnitPK() ); 28 (c) Libra 2001 public static void main(String [] args) { try { Client PortableRemoteObject.narrow . . . InitialContext context = new InitialContext(p); Object ref = context.lookup("ProductEJB"); ProductEJBHome productEJBHome = (ProductEJBHome) PortableRemoteObject.narrow(ref, ProductEJBHome.class); . . . Downcast und nicht: Object ref = context.lookup("ProductEJB"); ProductEJBHome productEJBHome = (ProductEJBHome) ref; . . . 29 (c) Libra 2001 Entity Bean ejbFindxxx Mit einer create() Methode auf dem Home Interface wird auf dem Server ein Objekt instantiiert und zugleich neu erzeugt – d.h. mit der Instantiierung des Objekts ist ein „neu Erstellen“ im persistenten Storage verbunden Objekte, die schon in der Datenbank vorhanden sind, werden dagegen durch Aufruf von find() Methoden auf dem Home Interface im Container Objekte „instantiiert“, d.h. in der Datenbank aufgefunden und als Bean Instanzen mit Datenbank Werten geladen einer findxxx() Methode auf dem Home Interface entspricht immer eine ejbFindxxx() Methode in der Bean jede Entity Bean muss mindestens eine findByPrimaryKey() Methode auf dem Home Interface und eine ejbFindByPrimaryKey() Methode in der Bean Implementierung haben 30 (c) Libra 2001 Bean Home Interface findByPrimaryKey (5153) int id String name; double price; ejbFindByPrimaryKey(...) Entity Bean Product find() Product ID ... Product Name Preis 5153 Cotes du Rhone 7.80 ... Remote Interface setPrice(. . .) getPrice() setPrice(double d) { price = d; } getPrice() { return price; } 31 (c) Libra 2001 public static void main(String [] args) { Client - Product find try { Properties p = new Properties(); ... InitialContext context = new InitialContext(p); Object ref = context.lookup("ProductEJB"); ProductEJBHome productEJBHome = (ProductEJBHome) PortableRemoteObject.narrow(ref, Home Interface ProductEJBHome.class); // find by primary key ProductPK pk = new ProductPK(5); Primary Key ProductEJB productEJB; try Remote Interface { productEJB = productEJBHome.findByPrimaryKey(pk); } catch (javax.ejb.FinderException e) { System.err.println("No object found for " + pk); return; } System.out.println("Product: id=" + productEJB.getId() + ", desc=" + productEJB.getDesc() + ", price=" + productEJB.getPrice() + ", measureUnitPK=" + productEJB.getMeasureUnitPK() (c) Libra 2001 ); 32 Product Home Interface public interface ProductEJBHome extends EJBHome { public ProductEJB create(String desc, double price, String measureUnitId) throws RemoteException, CreateException, DuplicateKeyException; public ProductEJB findByPrimaryKey(ProductPK productPK) throws RemoteException, FinderException; } 33 (c) Libra 2001 Bean – ejbFindByPrimaryKey() public ProductPK ejbFindByPrimaryKey(ProductPK pk) throws FinderException { Connection dbConnection = null; Statement stmt = null; ResultSet rs = null; String sql = "select PRODUCTNUM from PRODUCT " + "where PRODUCTNUM = " + pk.getId() ; try { try { dbConnection = getDbConnection(); stmt = dbConnection.createStatement(); rs = stmt.executeQuery(sql); if (rs.next()) { return pk; Primary Key } else { throw new javax.ejb.ObjectNotFoundException( "No product found in database for " + pk); } } finally { if (dbConnection != null) dbConnection.close(); } } catch (java.sql.SQLException e) { throw new EJBException ("SQL Exception in find by primary key" + e); } } 34 (c) Libra 2001 ejbFindByPrimaryKey() Aufgaben Die Aufgabe der ejbFindByPrimaryKey() Methode ist nur, den Primary Key, den die Methode schon als Parameter bekommen hat, zurückzugeben, falls ein entprechendes Objekt in der Datenbank vorhanden ist, d.h. im Grunde nur festzustellen, ob für den Primary Key ein Satz in der Datenbank vorhanden ist. Wird kein Satz in der Datenbank gefunden wird, muss die Methode eine FinderException werfen. Aufgabe ist nicht(!!!), die Werte aus der Datenbank zu laden und damit die Instanzvariablen zu setzen. Das ist Aufgabe der ejbLoad() Methode. 35 (c) Libra 2001 Bean – ejbLoad() public void ejbLoad() { ProductPK pk = (ProductPK)context.getPrimaryKey(); Connection dbConnection = null; String sql = "select * from PRODUCT " + "where PRODUCTNUM = " + pk.getId() ; Statement stmt = null; ResultSet rs = null; try { try { dbConnection = getDbConnection(); stmt = dbConnection.createStatement(); rs = stmt.executeQuery(sql); if (rs.next()) { this.id = rs.getInt("PRODUCTNUM"); this.desc = rs.getString("PRODUCTDESC"); this.price = rs.getDouble("PRICE"); this.measureUnitId = rs.getString("MEASUREUNITID"); } } finally { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (dbConnection == null) dbConnection.close(); } } catch (java.sql.SQLException e) { throw new EJBException (e); } } (c) Libra 2001 36 ejbLoad() Aufgaben ejbLoad() kann nicht vom Client, sondern allein vom Container aufgerufen werden wann ejbLoad() aufgerufen wird, ist durch die Sequenzdiagramme der EJB Spezifikation geregelt (bei Find Sequenzen wird ejbLoad() nach ejbFindxxx aufgerufen und bevor der Client Methoden des durch die Find Methode erhaltenen Remote Interface aufruft) Aufgabe ist, mit dem Primary Key aus dem Entity Context die Werte für das Objekt aus der Datenbank zu laden und die Instanzvariablen zu setzen. 37 (c) Libra 2001 Enity Context public void setEntityContext(EntityContext context) { this.context = context; } setEntityContext() ist eine Methode, die von der Bean implementiert werden muss. Sie wird vor ejbLoad() aufgerufen (genauer gesagt vor ejbActivate() oder vor ejbPostCreate()) Der Enity Context hat eine Methode getPrimaryKey(). Die getPrimaryKey() Methode gibt den Primary Key des EJB Objekts zurück, das mit der Bean Instanz assoziiert ist. 38 (c) Libra 2001 Product Remote Interface public interface ProductEJB extends EJBObject { public int getId() throws RemoteException; public String getDesc() throws RemoteException; public void setDesc(String desc) throws RemoteException; public double getPrice() throws RemoteException; public void setPrice(double price) throws RemoteException; public MeasureUnitPK getMeasureUnitPK() throws RemoteException; public void setMeasureUnitPK(MeasureUnitPK measureUnitPK) throws RemoteException; } Das Remote Interface enthält keine Methode „save“. Wenn das Server Objekt durch eine setXXX() Methode verändert wurde, kann der Client davon ausgehen, dass das Objekt persistent geändert wurde. Er braucht sich um die Persistenz der Änderung nicht zu kümmern. 39 (c) Libra 2001 public void ejbStore() { ProductPK pk = (ProductPK)context.getPrimaryKey(); Connection dbConnection = null; String sql = "update PRODUCT set " + "PRODUCTNUM = ?" + ", PRODUCTDESC = ?" + ", PRICE = ?" + ", MEASUREUNITID = ?" + " where PRODUCTNUM = " + pk.getId(); PreparedStatement stmt = null; try { try { dbConnection = getDbConnection(); stmt = dbConnection.prepareStatement(sql); int i = 1; stmt.setInt(i++, this.id); stmt.setString(i++, this.desc); stmt.setDouble(i++, this.price); stmt.setString(i++, this.measureUnitId); if (stmt.executeUpdate() == 1) return; else throw new SQLException("Error on update"); } finally { if (stmt != null) stmt.close(); if (dbConnection != null) dbConnection.close(); } } catch (java.sql.SQLException e) { throw new EJBException (e); } } Bean – ejbStore() 40 (c) Libra 2001 Implementierung der Business Methods – Trivia public int getId() { return this.id; } public String getDesc() { return this.desc; } public void setDesc(String desc) { this.desc = desc; } public double getPrice() { return this.price; } public void setPrice(double price) { this.price = price; } public MeasureUnitPK getMeasureUnitPK() { return new MeasureUnitPK(this.measureUnitId); } public void setMeasureUnitPK(MeasureUnitPK measureUnitPK) { this.measureUnitId = measureUnitPK.measureUnitId; } (c) Libra 2001 41 public class ProductEJBBean implements EntityBean { Entity Bean - Skelett public ProductPK ejbCreate (...) throws CreateException, DuplicateKeyException { // returns Primary Key Object // für jede create() Methode aus Home eine von der Signatur her passende // für einen Satz in die Datenbank, der die Bean repräsentiert // setzt die Instanz Variablen } public void ejbPostCreate(...) { // EJB object identity ist jetzt verfügbar (EntityContext.getEJBObject()) } public void ejbRemove() throws RemoveException { // entfernt Satz aus Datenbank, der die Bean repräsentiert } public void ejbLoad() { // synchronisiert Instanzvariable mit Satz aus Datenbank } public void ejbStore() { // schreibt Zustand der Instanzvariablen in Datenbank } public ProductPK ejbFindByPrimaryKey(ProductPK pk) throws FinderException { // locates one entity object in the database } public void setEntityContext(EntityContext context) { // container übergibt reference to EntityContext // bean allocates resources held for the bean's lifetime } public void unsetEntityContext() { // Freigabe von Ressource, die allokiert wurden in setEntityContext } public void ejbActivate() { // container associates EJB object identity with an instance // bean allocates additional resources needed in ready state } public void ejbPassivate() { // container dissociates identity from instance // bean frees resources allocated in ejbActivate } } (c) Libra 2001 42 Entity Bean – Zustandsablauf 1 Client: ProductEJB productCrt = productEJBHome.create("Cotes du Bourgogne", 7.8, "fl"); log EJB Server: [Default] schulung.app.product.ProductEJBBean: [Default] schulung.app.product.ProductEJBBean: [Default] schulung.app.product.ProductEJBBean: 104] [Default] schulung.app.product.ProductEJBBean: setEntityContext() null ejbCreate() null ejbPostCreate() [schulung.app.product.ProductPK: ejbStore() [schulung.app.product.ProductPK: 104] Client: System.out.println("Product: id=" + productCrt.getId() + ", desc=" + productCrt.getDesc() + ", price=" + productCrt.getPrice() + ", measureUnitPK=" + productCrt.getMeasureUnitPK() ); log EJB Server: [Default] schulung.app.product.ProductEJBBean: [Default] schulung.app.product.ProductEJBBean: [Default] schulung.app.product.ProductEJBBean: [Default] schulung.app.product.ProductEJBBean: ejbStore() ejbStore() ejbStore() ejbStore() [schulung.app.product.ProductPK: [schulung.app.product.ProductPK: [schulung.app.product.ProductPK: [schulung.app.product.ProductPK: 104] 104] 104] 104] Client: productCrt.remove(); log EJB Server: [Default] schulung.app.product.ProductEJBBean: ejbRemove() [schulung.app.product.ProductPK: 105] 43 (c) Libra 2001 Client: Entity Bean – Zustandsablauf 2 ProductPK pk = new ProductPK(5); productEJB = productEJBHome.findByPrimaryKey(pk); log EJB Server: [Default] schulung.app.product.ProductEJBBean: setEntityContext() null [Default] schulung.app.product.ProductEJBBean: ejbFindByPrimaryKey() [Default] schulung.app.product.ProductEJBBean: found [schulung.app.product.ProductPK: 5] Client: System.out.println("Product: id=" + productEJB.getId() + ", desc=" + productEJB.getDesc() + ", price=" + productEJB.getPrice() + ", measureUnitPK=" + productEJB.getMeasureUnitPK() ); log EJB Server: [Default] schulung.app.product.ProductEJBBean: setEntityContext() null [Default] schulung.app.product.ProductEJBBean: ejbActivate() [schulung.app.product.ProductPK: 5] [Default] schulung.app.product.ProductEJBBean: ejbLoad() [schulung.app.product.ProductPK: 5] [Default] schulung.app.product.ProductEJBBean: ejbStore() [schulung.app.product.ProductPK: 5] [Default] schulung.app.product.ProductEJBBean: ejbStore() [schulung.app.product.ProductPK: 5] [Default] schulung.app.product.ProductEJBBean: ejbStore() [schulung.app.product.ProductPK: 5] [Default] schulung.app.product.ProductEJBBean: ejbStore() [schulung.app.product.ProductPK: 5] Client: String desc = productEJB.getDesc().trim(); log EJB Server: [Default] schulung.app.product.ProductEJBBean: ejbStore() [schulung.app.product.ProductPK: 5] Client: desc = desc + "-1"; productEJB.setDesc(desc); log EJB Server: [Default] schulung.app.product.ProductEJBBean: ejbStore() [schulung.app.product.ProductPK: 5] 44 (c) Libra 2001 Deployment Descriptor ejb-jar.xml 1 <?xml version="1.0" encoding="UTF-8" ?> - <ejb-jar> <description>Libra edu Enterprise Beans</description> <display-name>Libra edu Enterprise Beans</display-name> - <enterprise-beans> - <entity> <description>product</description> <ejb-name>ProductEJB</ejb-name> <home>schulung.app.product.ProductEJBHome</home> <remote>schulung.app.product.ProductEJB</remote> <ejb-class>schulung.app.product.ProductEJBBean</ejbclass> <persistence-type>Bean</persistence-type> <prim-key-class>schulung.app.product.ProductPK</prim-keyclass> <reentrant>False</reentrant> - <resource-ref> <description>Datasource for Product EJB</description> <res-ref-name>jdbc/edu</res-ref-name> <res-type>javax.sql.DataSource</res-type> <res-auth>Container</res-auth> </resource-ref> </entity> 45 (c) Libra 2001 <?xml version="1.0" encoding="UTF-8" ?> - <ejb-jar> - <enterprise-beans> ... </enterprise-beans> - <assembly-descriptor> - <container-transaction> - <method> <ejb-name>ProductEJB</ejb-name> <method-name>*</method-name> </method> <trans-attribute>Required</trans-attribute> </container-transaction> - <container-transaction> - <method> <ejb-name>ProductEJB</ejb-name> <method-name>findByPrimaryKey</method-name> </method> <trans-attribute>NotSupported</trans-attribute> </container-transaction> - <container-transaction> - <method> <ejb-name>ProductEJB</ejb-name> <method-name>getDetail</method-name> </method> <trans-attribute>NotSupported</trans-attribute> </container-transaction> </assembly-descriptor> </ejb-jar> Deployment Descriptor ejb-jar.xml 2 46 (c) Libra 2001 Jar File EJB Server Deployment META-INF/ejb-jar.xml edu.jar ProductEJB.class ProductEJBHome.class ProductEJBBean.class ProductPK.class jar cvf edu.jar META-INF/ejb-jar.xml META-INF/jboss.xml schulung/app/product/*.class copy edu.jar "\jboss-2.4.3\deploy" 47 (c) Libra 2001 J2EE Patterns DAO (Data Access Object) Auslagerung des SQL Codes in eine eigene Klasse Value Objects Bündelung von Attributen in einer Klasse -> „Core J2EE Patterns – Best Practices and Design Strategies“ by Alur / Cupri / Malks ISBN 0-13-064884-1 48 (c) Libra 2001 EJB 2.0 – Entity Beans CMP Modell hat sich erheblich verbessert Container managed Relations zwischen Entity Beans Neben Remote Interfaces auch Local Interfaces Query Language für Entity Beans 49 (c) Libra 2001 Session Beans Session Beans werden dazu verwendet, um serverseitige Aufgaben im Auftrag des Clients auszuführen, Ressourcen (wie Kurse und Tabellen) zugänglich zu machen und das Zusammenspiel von Entity Beans zu organisieren. Session Beans haben keine Persistenz und repräsentieren keine Daten aus einer Datenbank (sehr wohl kann es aber zu ihren Aufgaben gehören, Daten aus der Datenbank zu lesen und zu schreiben) und enkapsulieren typischerweise die Business Logik einer Anwendung. Typische Beispiele für Session Beans sind: • Warenkorb, der sich während einer Session aufbaut • Währungsumrechner, Buchungsprozesse 50 (c) Libra 2001 Es gibt zwei grundsätzliche Arten von Session Beans: Stateless und Stateful. Stateless Session Beans behalten keine Zustandsinformation von einem Aufruf zum nächsten. Man kann sie wie Funktionsaufrufe verstehen, die allein mit den übergebenen Parametern arbeiten: Eingabe von einer Währung – Ausgabe in Euro Stateful – Stateless Session Beans Stateful Session Beans behalten einen Zustand von Aufruf zu Aufruf. Wenn einem Warenkorb ein Artikel hinzugefügt wird, ist dieser auch beim nächsten Hinzufügen noch erhalten. Eine "stateful" Session Bean spiegelt den Zustand einer client-seitigen "Session" wider. Der Container kann (aus Container Management Gründen) eine Session Bean zeitweise aus dem Memory nehmen und in einen Secondary Storage (Disk) schreiben. Dieser Transfer wird Instanz "Passivation" genannt. Der umgekehrte Transfer ist "Activation". Der Container sollte eine Session Bean nur passivieren, wenn sie nicht Teil einer Transaktion ist. Damit der Container den "State" einer Session Bean besser managen kann, wird ihm im Deployment Descriptor der Bean mitgeteilt, ob es sich um eine STATELESS oder STATEFUL Session Bean handelt. Bei STATELESS Session Beans bleibt kein Zustand zwischen Methoden Aufrufen vorhanden und kann eine Bean Instanz von jedem Client verwendet werden. Bei STATEFUL Session Beans muss der Zustand der Bean zwischen Methoden und über Transaktionsgrenzen hinweg erhalten bleiben. 51 (c) Libra 2001 OrderController Session Bean OrderController SessionBean List positions; Home Interface create(); Remote Interface addProduct( ProductPK pk); getTotalPrice(); ejbCreate() {...} addProduct(ProductPK pk) { productEJB = productHome. findByPrimaryKey(pk); productDetail = productEJB.getDetail(); positions.add(productDetail); } Product EntityBean int id String name; double price; Home Interface findByPrimaryKey (5153) ejbFindByPrimaryKey(...) Remote Interface getDetail() P ro du ct ID . . . P ro du ct N am e Preis 5 15 3 C otes d u R h one 7.80 . . . (c) Libra 2001 getDetail() { return new ProductDetail(id, name, price..); } 52 SessionBean - Skelett public class OrderControllerEJBBean implements SessionBean { public void ejbCreate () throws CreateException { } public void ejbRemove() throws RemoveException { } public void setEntityContext(EntityContext context) { // container übergibt reference to EntityContext // bean allocates resources held for the bean's lifetime } public void unsetEntityContext() { // Freigabe von Ressource, die allokiert wurden in setEntityContext } public void ejbActivate() { // container associates EJB object identity with an instance // bean allocates additional resources needed in ready state } public void ejbPassivate() { // container dissociates identity from instance // bean frees resources allocated in ejbActivate } Kein public public public public void ejbPostCreate(...) {} void ejbLoad() {} void ejbStore() {} ... ejbFindByPrimaryKey(...) throws FinderException {} } 53 (c) Libra 2001 public class OrderControllerEJBBean implements SessionBean { private SessionContext context; private javax.naming.InitialContext jndiContext; private List positions; public void ejbCreate() throws RemoteException, CreateException { positions = new ArrayList(); } OrderController SessionBean public void addProduct(ProductPK productPK) { // check for duplicates Iterator it = positions.iterator(); while (it.hasNext()) { OrderPos pos = (OrderPos) it.next(); Product product = pos.getProduct(); if (product.getPK().equals(productPK)) { pos.setQuantity( pos.getQuantity() + 1); return; } } // find product and add it to positions try { ProductEJBHome productHome = getProductEJBHome(); ProductEJB productEJB = productHome.findByPrimaryKey(productPK); OrderPos pos = new OrderPos(); pos.setProduct(productEJB.getDetail()); pos.setQuantity(1); positions.add(pos); } catch (javax.ejb.FinderException e) { throw new EJBException(e); } catch (java.rmi.RemoteException e) { throw new EJBException(e); } (c) Libra} 2001 54 Pro Gründe für und gegen den Einsatz von EJB Anwendungssysteme lassen sich schneller und zuverlässiger aufbauen. EJB Komponenten profitieren von einer deklarativen Middleware, mit der sich Instance Pooling, Transaktionen, Security, Persistenz, Relationships (EJB 2.0) und Caching einstellen lassen. Auf der Basis von Standard Java allein, müsste man diese Mechanismen und Middleware selbst entwicklen. EJB ist ein verlässlicher und getesteter Standard. EJB ist ein etablierter Standard. Es gibt Entwickler und Projektsoftwarehäuser auf dem Markt, die die Technologie beherrschen. Bei eigenentwickelter Middleware können neue Entwickler nur mit langen Vorlaufzeiten eingestellt werden. Jeder der auf Basis von EJB Technologie Anwendungen entwickelt, profitiert von der weltweiten Erfahrung im Umgang mit der Technolgie. Aus dieser Erfahrung haben sich bereits dokumentierte Patters ergeben. EJB sind eine zentrale Anwendungsschnittstelle für technologisch unterschiedliche Clients. Es gibt eine Vielzahl von Entwicklungswerkzeugen für EJB Technolgie. Aus UML Diagarammen können EJB automatisch generiert werden. Web und Applikationsserver können netzwerktechnisch getrennt werden. Contra Wenn die Anwendung einfach ist, kann EJB Technologie Overkill sein. (c) Libra 2001 55 EJB Server IBM Websphere BEA Weblogic JBoss (www.jboss.org) Open Source + lizenzfrei 56 (c) Libra 2001 JBoss Entstanden 1999 - JBoss ist das Produkt einer OpenSource Developer Community die das erklärte Ziel hatte, den besten J2EEcompliant Applikationsserver auf dem Markt zu entwickeln 1000 Developer weltweit Jboss ist wahrscheinlich der verbreiteste EJB Server der Welt heute 4000 Downloads des EJB Servers pro Tag (mehr als manche kommerzielle Hersteller insgesamt haben) Die Distrubition 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. 57 (c) Libra 2001 Unternehmen und Institutionen, die JBoss einsetzen AbaXX, Germany, ABC Virtual Communications, Accenture, Acxiom, AdvancePCS Inc., Agilent, Agility Mgmt. Partners, Ardec, Argotec Germany, BaaN, BAE Systems, Bank of America, Baring Asset Management, BellSouth, Benefit AB Sweden, Boeing, Bouygues Telecom, British Petroleum UK, Cardinal Health, Cepsa Spain , CGDA, Compaq, Computer Associates, Coremetrics, Coritel, CPW UK, CSC, CSIRO, Data Experts Gmbh Germany, Datenzentrale, DCI Ltd. UK, Decell Israel, Deutschebank AG, Directory Technology Pty Ltd, Dow Jones & Co., DP AG, Dresdner Kleinwort Wasserstein UK, Earthlink Inc, Epixtech Corp., FGM Corp., Finland Post Corporation, Frontwire UK, Fujixerox Japan, GE Capital TIP, General dynamics/Techsight, Highmark, Inc., Getronics Govt. Solutions, Highbridge, Home Depot, Honeywell, IcomXpress, Ifm electronic gmbh, IGT, IHS Accumap, Informatique CDC, DTA, Informativi S.p.A., Integris, KB, Ivertexo Internet Solutions, JP Morgan, KPMG Consulting, LandSafe, Logica, Lexign, Maxim Group Business Solutions, MBT, McLeodUSA, Mercury Interactive, Michigan State University, MIT, Mitre Corp., Motorola, NASA, National Computer System Peregrine Systems, Norsk Eiedomsinformasjon Norway, Novell, Nuasis, Orange Communications Switzerland, Politet Datajeneste Norway, Pracom, Private, Proximo, Rational Software, Raytheon, Riva Technologies Inc., Rivcoll Union, RoadRunner Broadband, Hoffmann-La Roche, Schlumberger, Sempra Energy Trading, Sextantech Technology Consulting, Shiftat Bvba Belgium, Siemens AG, Silverline, Sisa Studio Switzerland, Sistemi, SolidSource Corp., Soltima Czech Republic, Spirent Systems, Sprint PCS, Statoil, Stilo Technology UK, Summit Information Systems, Telenor Network Services, Tenovis Germany, TietoEnator Consulting, TietoEnator Corp., Triversity Inc., University of Bremen Germany, University of Florida, UPB, USCO Logistics, Verizon, Viant Intl. Corp.UK, Videotron, Volkswagen Germany, Walkers Snack Foods UK, Winterthur Insurance, WM-data, World Travel Partners, WorldCom, XML Asia Ltd. Hong Kong, Zantaz Corp., 58 (c) Libra 2001 Trademarks Sun, Sun Microsystems, the Sun logo, Java, Jini, J2EE, JavaServer Pages, Enterprise JavaBeans, Java Compatible, JDK, JDBC, JavaBeans, JavaMail, Write Once, Run Anywhere, and Java Naming and Directory Interface are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. IBM and WebSphere are trademarks of International Business Machines Corporation in the United States, other countries, or both. iSeries and the e-business logo are trademarks of International Business Machines Corporation in the United States or other countries or both. OS/400 and AIX are registered trademarks of International Business Machines Corporation in the United States or other countries or both. Urls http://java.sun.com/products/ejb/ http://java.sun.com/j2ee/ http://www.jboss.org 59 (c) Libra 2001