Johann Mildner, Basel JEE – Business Component Developer Seminar JEE Business Component Developer Seminar GLASSFISH 01.01.2017 18.05.17 8:44 1 Johann Mildner, Basel JEE – Business Component Developer Seminar JEE - Business Component Developer Seminar Stellen Sie sich folgende Aufgabenstellung vor: Ein Programm benötigt zu einer bekannten Personalnummer verschiedene Daten wie Name, Adresse, Name der Ehefrau und der Kinder. Sie wissen, dass diese Daten in den Tabellen MITARBEITER, ADRESSEN, EHEGATTEN und KINDER zu finden sind. Kein Problem werden Sie sagen und wahrscheinlich einen SELECT MITARBEITER join ADRESSEN join EHEGATTEN, sowie einen SELECT KINDER absetzten um diese Daten zu lesen. Sie werden diese Daten mittels einer Methode MaDaten.holeMitarbeiterdaten(int ID) holen. Da MaDaten temporäre Daten sind, werden Sie diese Daten in einer inneren Klasse erzeugen. War doch gar nicht so schwierig. Wie es so ist, benötigen Sie diese Daten in einem weiteren Programm. Kein Problem: die innere Klasse wird zu einer richtigen Klasse und Sie verwenden diese Klasse ein zweites Mal. Bleibt ja in der Familie. Es kommt wie’s kommen muss, in einem anderen Projekt werden diese Daten ebenfalls benötigt. Jetzt wird’s kompliziert. Die Daten befinden sich auf Ihrem Rechner. Es soll aber von einem entfernten Rechner auf diese Daten zugegriffen werden. Die zündende Idee: Sie schreiben einen Socket-Server und das andere Projekt schreibt einen Socket-Client und schon ist die Sache geritzt. Erste Gespräche ergeben, dass Ihre Kollegen vom anderen Projekt noch nie was von Sockets gehört haben. Sie sind froh darüber denn diese Idee wäre doch nicht so gut gewesen. Viel zu aufwändig. RMI wäre da schon eher geeignet aber das kennen die Kollegen vom anderen Projekt eigentlich auch nicht wirklich. Java EE bietet für verteilte Anwendungen Enterprise Java Beans (EJB) als Lösung an1. Dieses Seminar führt Sie nun in die Technik der EJB-Programmierung ein. So wie Servlets und JSP’s einen Servlet-Container benötigen, benötigen EJB’s einen EJBContainer. Dieser EJB Container befindet sich innerhalb eines Applikationsservers. Dieses Seminar zeigt Ihnen, wie Sie verteilte Anwendungen realisieren. Sie werden Session Beans (Stateless und Stateful), Entities (JPA – Java Persistence API), Message Driven Beans, Message Services, Timer Services sowie Web Services kennenlernen. Es werden Aufgaben gestellt, die als EJB‘s gelöst werden. Der Aufruf dieser EJBs kann über Konsolprogramme, SWING Programme und Web Komponenten (Servlet, JSP, JSF) erfolgen. 1 Nach einiger Überlegung würde sich für dieses speziell konstruierte Szenario auch noch ein Web-Service anbieten. 18.05.17 8:44 2 Johann Mildner, Basel JEE – Business Component Developer Seminar Übersicht JEE JEE – Java Enterprise Edition ist ein Standard für die Programmierung verteilter Anwendungen. Wann ist eine Anwendung verteilt? Wenn Client und Server auf verschiedenen Rechnern laufen. Verteilung liegt aber auch dann vor, wenn Client und Server zwar auf dem gleichen Rechner aber in verschiedenen VM’s laufen. Client nutzt einen Dienst der auf einem Server läuft. Diese serverseitigen Programme sind sogenannte Enterprise Java Beans (EJB’s). Arten von Beans: Session Beans – local und remote (Session = Sitzung) o Stateless Session Beans o Stateful Session Beans Message Driven Beans Entities Java Message Service Timer Service Web Services (Stateless Session Beans mit @Annotations) Clients: Konsolprogramme andere EJB’s GUI Programme (SWING) Web Components (JSF/JSP/Servlet) 18.05.17 8:44 3 Johann Mildner, Basel JEE – Business Component Developer Seminar Inhaltsverzeichnis JEE - Business Component Developer Seminar ..................................................................................2 Übersicht JEE ..................................................................................................................................3 Inhaltsverzeichnis ................................................................................................................................4 Vorbereitung ........................................................................................................................................6 Software installieren ........................................................................................................................6 Projekte vorbereiten .........................................................................................................................7 Teil 1 Session Beans ............................................................................................................................8 Die erste Bean – First .....................................................................................................................8 Konventioen .................................................................................................................................8 Test der Bean aus Web Komponenten.........................................................................................9 Test der Bean aus einem Konsolprogramm ...............................................................................10 Helperklasse ...............................................................................................................................12 Statless Session Beans .................................................................................................................14 Remote Stateless Session Bean – Rechner ................................................................................14 Local Stateless Session Bean – Hallo1 und Motto1 ..................................................................16 Test der Local Stateless Session Bean .......................................................................................18 Stateful Session Beans – Messwerte ...........................................................................................19 Test der Stateful Session Bean ..................................................................................................20 Aufruf einer EJB aus einer anderen EJB des selben EJB Containers ........................................21 Übung zu Session Beans (optional) ...........................................................................................21 Warenkorb .................................................................................................................................21 Teil 2 Entity Beans ............................................................................................................................22 Vorbereitung ................................................................................................................................22 Applikationsserver .....................................................................................................................22 Projekt ........................................................................................................................................24 Entity – Person1 ...........................................................................................................................25 Data Access Object – Person1Dao ...............................................................................................26 Test Person1Dao ..........................................................................................................................28 JPA (Java Persistence API) ...........................................................................................................28 Teil 3 Transaktionen ..........................................................................................................................29 CMT – Container Managed Transaction .....................................................................................29 Übung zu CMT ..........................................................................................................................30 BMT – Bean Managed Transaction .............................................................................................31 Person2Dao ................................................................................................................................32 ReiseDao ....................................................................................................................................35 Teil 4 Java Message Service ..............................................................................................................39 Funktionsweise ............................................................................................................................39 Vorbereitung für GLASSFISH4 ....................................................................................................40 Applikationsserver .....................................................................................................................40 Implementierung ........................................................................................................................41 Message Driven Bean ...................................................................................................................43 Senden........................................................................................................................................43 Empfangen .................................................................................................................................45 Teil 5 Timer Service ..........................................................................................................................46 Übung1 zu Timer Service ............................................................................................................49 Übung2 zu Timer Service (optional) .........................................................................................49 Teil 6 Web Services ...........................................................................................................................50 Service ...........................................................................................................................................50 Client..............................................................................................................................................50 18.05.17 8:44 4 Johann Mildner, Basel JEE – Business Component Developer Seminar Teil 7 Life Cycle, Life Cycle Callback ..............................................................................................51 Stateless Session Bean, Message Driven Bean..............................................................................51 Life Cycle ..................................................................................................................................51 Life Cycle Callbacks..................................................................................................................51 Stateful Session Bean ....................................................................................................................52 Life Cycle ..................................................................................................................................52 Life Cycle Callbacks..................................................................................................................52 Entities ...........................................................................................................................................53 Life Cycle und Life Cycle Callbacks .........................................................................................53 Teil 8 Interzeptoren............................................................................................................................54 Teil 9 Was noch nicht behandelt wurde ............................................................................................57 LocalBeans ....................................................................................................................................57 Beans mit asynchronen Methoden .................................................................................................57 18.05.17 8:44 5 Johann Mildner, Basel JEE – Business Component Developer Seminar Vorbereitung Es wird erwartet, dass Sie ein erfahrener Java Entwickler sind und Ihre IDE im Griff haben. Sie haben schon Java Programme unter Zuhilfenahme einer IDE geschrieben. Sie kennen SQL und können einigermassen mit Datenbanken umgehen. In diesem Abschnitt werden wir unsere Vorbereitungsarbeiten erledigen. Software installieren 1) Java 1.8 Downloaden Sie Java 1.8 JDK aus dem Internet Installieren Sie Java 1.8 JDK Setzen der Umgebungsvariablen $JAVA_HOME und $PATH 2) APPLIKATIONSSERVER Downloaden Sie GLASSFISH Full Platform aus dem Internet Entzippen Sie GLASSFISH in das Verzeichnis __BCDS/utils Starten Sie GLASSFISH mittels .../bin/startserv.bat Stoppen Sie GLASSFISH mittels .../bin/stopserv.bat Testen Sie in einem Browser http://localhost:8080, http://localhost:4848 3) DATENBANK Downloaden Sie die Datenbank H2 aus dem Internet Kopieren Sie aus .../bin die Jar-Datei h2-1.3.176.jar in das Verzeichnis Starten Sie H2 indem die h2-1.3.166.jar doppelklicken Setzen Sie für den User sa das Passwort auf sa alter user sa set password 'sa' Stoppen Sie H2 rechtsklick H2 Icon im System Tray - exit 4) ECLIPSE JEE Downloaden Sie ECLIPSE aus dem Internet Entzippen Sie ECLIPSE in das Verzeichnis Starten und Stoppen Sie ECLIPSE __BCDS/lib __BCDS/utils __BCDS/workspace Fügen Sie dem Eclipse den Glassfish Applikationsserver hinzu: die Glassfish Tools Server new/Server/Oracle/GlassFish Tools next accept finish restart den Glassfish Applikationsserver Server new/Server/GlassFish/GlassFish4 next Location: .../utils/glassfish4/glassfish next next finish Starten und Stoppen sie den Glassfish Server aus Eclipse Server GlassFish 4 at localhost [domain1] rechtsklick Start/Restart/Stop 18.05.17 8:44 6 Johann Mildner, Basel JEE – Business Component Developer Seminar Projekte vorbereiten Wir benötigen in Eclipse folgende Projekte 1) bcdsEJB das Projekt für die Enterprise Java Beans New / Other / EJB / EJB Project Project Name: bcdsEJB Next / Next Set Generate ejb-jar.xml deployment descriptor Finish 2) bcdsWEB das Projekt für den WebTest der EJB’s New / Other / Web / Dynamic Web Project Project Name: bcdsWEB Next / Next Set Generate web.xml deployment descriptor Finish 3) bcdsEAR das Enterprise Archiv für EJB und WEB New / Other / Java EE / Enterprise Application Project Project Name: bcdsEAR Next Java EE module dependencies Set bcdsEJB Set bcdsWEB Set Generate application.xml deployment descriptor Finish 4) bcdsXTest das Projekt für die Remote Tests der EJB’s New / Other / Java / Java Project Project Name: bcdsXTest Finish 18.05.17 8:44 7 JEE – Business Component Developer Seminar Johann Mildner, Basel Teil 1 Session Beans Die erste Bean – First Die erste Enterprise Java Bean dient uns dem Kennenlernen der Konventionen. Ein Dienst besteht aus einem Interface und seiner Implementierung. Unser erster Dienst soll First heissen und eine Methoden anbieten, die einen String übernimmt und diesen als Echo wieder ausgibt. Konventioen Name des Dienstes: Interface: Implementierung: Methode(n): First FirstBeanRemote.java FirstBean.java String echo(String s) interfaces/FirstBeanRemote.java package ch.bcds1.interfaces; @Remote public interface FirstBeanRemote { String echo(String s) ; } beans/FirstBean.java package ch.bcds1.beans; @Stateless public class FirstBean implements FirstBeanRemote { @Override public String echo(String s) { return s + "/" + s ; } } 18.05.17 8:44 8 Johann Mildner, Basel JEE – Business Component Developer Seminar Test der Bean aus Web Komponenten Die können die Beans sowohl aus einem Servlet als auch aus einem JSP aufrufen. Schreiben Sie Ihre Web Komponenten in ein eigenes Web Projekt (bcdsWEB). Damit der Compile fehlerfrei laufen kann müssen die Glassfish.libs und die Interfaces und Entities im CLASSPATH verfügbar sein Projects Add bcdsEJB Beispiel für Servlet FirstServlet.java package ch.bcds1.servlet; @WebServlet("/FirstServlet") public class FirstServlet extends HttpServlet { private static final long serialVersionUID = 1L; @EJB private FirstBeanRemote bean; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setHeader("Content-Type", "text/html"); PrintWriter pw = response.getWriter(); pw.println("<h1>FirstServlet</h1>"); pw. println (bean.echo("hallo aus einem Servlet")); } } Beispiel für JSP firstJsp.jsp <%@ page import="javax.naming.*,ch.bcds1.interfaces.*,ch.bcds.tools.*"%> <h1>FirstJSP</h1> <% InitialContext ctx = new InitialContext(); String LOOKUP_NAME = "java:global/bcdsEAR/bcdsEJB/FirstBean!ch.bcds1.interfaces.FirstBeanRemote"; FirstBeanRemote bean = (FirstBeanRemote) ctx.lookup(LOOKUP_NAME); out.println(bean.echo("hallo aus jsp")); %> 18.05.17 8:44 9 Johann Mildner, Basel JEE – Business Component Developer Seminar Test der Bean aus einem Konsolprogramm Schreiben Sie Ihre Tests in ein eigenes Testprojekt (bcdsXTest). FirstTest.java package ch.bcds1.test; public class FirstTest { public static void main(String[] args) throws NamingException { /** * 1. * * <code> * damit die Imports funktionieren ist folgendes erforderlich * * Properties / Java Build Path * * Libraries / Add External JARs * $GLASSFISH/lib/gf-client.jar * * Projects * Add * bcdsEJB * </code> */ /* 2. Context erstellen */ InitialContext ctx = new InitialContext(); /* 3. Lookup und Bean fuellen */ final String LOOKUP_NAME = "java:global/bcdsEAR/bcdsEJB/FirstBean!ch.bcds1.interfaces.FirstBeanRemote"; FirstBeanRemote bean = (FirstBeanRemote) ctx.lookup(LOOKUP_NAME); /* 4. Aufruf der EJB Methode(n) */ System.out.println(bean.echo("hallo")); } } 18.05.17 8:44 10 Johann Mildner, Basel JEE – Business Component Developer Seminar 1) Damit der Compile fehlerfrei laufen kann müssen die Glassfish.libs und die Interfaces und Entities im CLASSPATH verfügbar sein a. gf-client.jar aus $GLASSFISH/lib (Libraries/Add External JARs b. die Interfaces und Entities aus bcdsEJB (Projects Add) 2) Damit die Referenz auf den Context des Applikationsservers ermittelt werden kann, ist die Datei jndi.properties nötig. Diese Datei mus im CLASSPATH gefunden werden. Es bietet sich an, ein Source Verzeichnis resources zu erstellen. Eventuell nicht mehr nötig. (19.12.2016) resources/jndi.properties java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory java.naming.factory.url.pkgs=com.sun.enterprise.naming java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl org.omg.CORBA.ORBInitialHost=localhost org.omg.CORBA.ORBInitialPort=3700 3) Der Aufbau des LOOKUP_NAME ist für alle Applikationsserver unterschiedlich. Glassfish hat den folgenden Aufbau: java:global/appName/modulName/BeanName!path.InterfaceName oder java:global/modulName/BeanName!path.InterfaceName java:global/bcdsEAR/bcdsEJB/FirstBean!ch.bcds.interfaces.FirstBeanRemote kann und soll in einer Helper Klasse erfolgen 18.05.17 8:44 11 Johann Mildner, Basel JEE – Business Component Developer Seminar Helperklasse Schreiben Sie für Ihre Tests zur Ermittlung der Bean Referenz in das bcdsWEB Projekt eine Helperklasse. Die Helperklasse kann auch in bcdsXTest genutzt werden. Helper.java package ch.bcds.tools; public final class Helper { public final static String makeLookupName(final String BEAN_NAME, final String INTERFACE_NAME) { /** * Beispiel des Lookup Namens fuer den Service "First" * <code> * java:global * /bcdsEAR Appliation Name * /bcdsEJB Modul Name * /FirstBean Bean Name * !ch.bcds.interfaces.FirstBeanRemote Interface Name * </code> * * Da Application Name und Modul Name fuer das gesamte Project * gleich sind, werden sie fest "verdrahtet" */ final String APP_NAME = "/bcdsEAR"; final String MOD_NAME = "/bcdsEJB"; return "java:global" + APP_NAME + MOD_NAME + "/" + BEAN_NAME + "!" + INTERFACE_NAME; } public static Object lookup(final String BEAN_NAME, final String INTERFACE_NAME) { try { return new InitialContext() .lookup(makeLookupName(BEAN_NAME, INTERFACE_NAME)); } catch (NamingException e) { e.printStackTrace(); return null; } } } 18.05.17 8:44 12 Johann Mildner, Basel JEE – Business Component Developer Seminar In den folgenden Testprogrammen soll das Füllen der EJB Referenz auf folgende Art gemacht werden. ... FirstBeanRemote bean = (FirstBeanRemote) Helper.lookup("FirstBean", "ch.bcds1.interfaces.FirstBeanRemote"); ... 18.05.17 8:44 13 JEE – Business Component Developer Seminar Johann Mildner, Basel Statless Session Beans Remote Stateless Session Bean – Rechner Das nächste EJB wird ein Dienst sein, der die vier Grundrechnungsarten anbietet. Name des Dienstes: Interface: Implementierung: Methode(n): Rechner RechnerBeanRemote RechnerBean double add(double a, double b) double sub(double a, double b) double mult(double a, double b) double dib(double a, double b) interfaces/RechnerBeanRemote.java package ch.bcds1.interfaces; @Remote public interface RechnerBeanRemote { double add(double a, double b); double sub(double a, double b); double mult(double a, double b); double div(double a, double b); } beans/RechnerBean.java package ch.bcds1.beans; @Stateless public class RechnerBean implements RechnerBeanRemote { @Override public double add(double a, double b) { return a + b; } @Override public double sub(double a, double b) { return a - b; } … } 18.05.17 8:44 14 Johann Mildner, Basel JEE – Business Component Developer Seminar Interface: @Remote public interface RechnerBeanRemote Die Annotation @Remote bewirkt dass der Dienst zu einem Dienst wird, der von einem entfernten Client aufgerufen werden kann. @Local bedeutet dass der Dienst nur aus einem Programm, das im selben AS läuft, aufgerufen werden kann (z.B. aus einer anderen EJB). Implementierung @Stateless public class RechnerBean implements RechnerBeanRemote Die Annotation @Stateless bewirkt, dass der Dienst keinen Status speichert. Das heisst, dass keine Attribute erlaubt sind die über mehrere Methodenaufrufe benötigt werden. @Stateful bedeutet, das die Bean Daten über mehrere Methodenaufrufe behält. Der Klient hat immer die selbe Referenz der Bean. Schreiben Sie Tests (Konsolprogramm, JSP, Servlet) 18.05.17 8:44 15 JEE – Business Component Developer Seminar Johann Mildner, Basel Local Stateless Session Bean – Hallo1 und Motto1 Als zweites EJB werden wir einen Dienst erstellen, der eine Hallo Meldung zurückgibt. Dieser Dienst soll wie der vorherige, Remote sein. Die Hallo Bean soll aber einen weiteren Dienst nutzen. Und zwar soll zu jedem Hallo ein Motto des Tages mitgegeben werden. Dieses Motto soll von einem lokalen Dienst ermittelt werden (Zufallsprinzip). Es sollen zwei Dienste erstellt werden. Name des Dienstes: Interface: Implementierung: Methode(n): Motto1 Motto1BeanLocal Motto1Bean String getMotto() (Local, Stateless) Motto1BeanLocal.java package ch.bcds1.interfaces; @Local public interface Motto1BeanLocal { String getMotto(); } Motto1Bean.java package ch.bcds1.beans; @Stateless public class Motto1Bean implements Motto1BeanLocal { private String[] motto = {"luegen haben kurze beine", "eile mit weile", "…") ; @Override public String getMotto() { return motto[(int) (Math.random() * motto.length)]; } } 18.05.17 8:44 16 JEE – Business Component Developer Seminar Johann Mildner, Basel Name des Dienstes: Interface: Implementierung: Methode(n): Hallo1 (Remote, Stateless) Hallo1BeanRemote Hallo1Bean String sagHallo() String sagByeBye() Hallo1BeanRemote.java package ch.bcds1.interfaces; @Remote public interface Hallo1BeanRemote { String sagHallo(); } Hallo1Bean.java package ch.bcds1.beans; @Stateless public class Hallo1Bean implements Hallo1BeanRemote { @EJB private Motto1BeanLocal motto; } @Override public String sagHallo() { return "Hallo Welt: " + motto.getMotto(); } 18.05.17 8:44 17 Johann Mildner, Basel JEE – Business Component Developer Seminar Test der Local Stateless Session Bean Eine Bean mit der Annotaion @Local kann nur von einem Client, der innerhalb des Applikationsservers läuft, aufgerufen werden. Es sind dies andere Beans, Java Server Faces, Servlets, Backing Beans aus JSP oder JSF Projects ... In unserem Fall von der EJB Hallo1Bean. Es ist dazu eine Deklaration mit Annotation nötig. @EJB private Motto1BeanLocal motto; Dies bewirkt dass motto durch Dependency Injection zum Zeitpunkt des Erzeugens des Hallo1Beans gefüllt wird Motto1BeanLocal muss nicht jedes mal neu erzeugt werden. Versehen Sie diese Bean mit @Singleton. Fügen Sie der Hallo1Bean eine fortlaufende ID und einen Zähler hinzu. Füllen Sie die ID in einer Methode mit der Annotation @PostConstruct indem Sie static int anzahl um 1 hochzählen. Geben Sie bei jedem Aufruf von getHallo() die ID und den um 1 erhöhten Zähler mit der Meldung aus. Testen Sie in einer Schleife – was stellen Sie fest? Testen Sie parallel (mehrere Threads) – was stellen Sie jetzt fest? Ändern Sie @Stateless des Hallo1Beans auf @Stateful. Testen Sie nochmals. Besser? 18.05.17 8:44 18 JEE – Business Component Developer Seminar Johann Mildner, Basel Stateful Session Beans – Messwerte Was wenn ein Dienst benötigt wird, der sich Zustände über mehrere Methodenaufrufe hinweg merken soll. Sammeln von Messwerten zum Beispiel. Dann verwendet man Stateful Session Beans. Name des Dienstes: Interface: Implementierung: Methode(n): Messwerte (Remote, Stateful) MesserteBeanRemote MesswerteBean void einfuegen(int key, int value) Map<Integer, Integer> getMesswerte() Die Methode einfuegen() dient dem Sammeln der Messwerte und soll beliebig oft aufgerufen werden können. Die Methode getMesswerte() dient dem Holen aller bisher gesammelten Messwerte. Diese Methode soll die Bean beenden (@Remove). Nach dem Aufruf dieser Methode ist die Bean nicht mehr verfügbar sein. MesswerteBeanRemote.java package ch.bcds1.interfaces; @Remote public interface MesswerteBeanRemote { void einfuegen(int key, int value); Map<Integer, Integer> getMesswerte(); } MesswerteBean.java package ch.bcds1.beans; @Stateful public class MesswerteBean implements MesswerteBeanRemote { private Map<Integer, Object > messwerte = new TreeMap<Integer, Integer >(); @Override public void einfuegen(int key, int value) { messwerte.put(key, value); } } @Override @Remove public Map<Integer, Integer > getMesswerte() { return messwerte; } 18.05.17 8:44 19 Johann Mildner, Basel JEE – Business Component Developer Seminar Test der Stateful Session Bean MesswerteTest.java package ch.bcds1.test; public class MesswerteTest { static final Logger LOG = Logger.getLogger(MesswerteTest.class); public static void main(String[] args) throws Exception { LOG.info("Programmstart"); MesswerteBeanRemote messwerte = (MesswerteBeanRemote) getBean(); for (int i = 11; i <= 20; i++) messwerte.einfuegen(i, (int) (Math.random() * 1000000)); MyTools.pause(); Map<Integer, Integer> messwerteMap = messwerte.getMesswerte(); for (Map.Entry<Integer, Integer> me : messwerteMap.entrySet()) { LOG.info(me.getKey() + " / " + me.getValue()); } } } LOG.info("Programmende"); private static Object getBean() { return Helper.lookup("MesswerteBean", "ch.bcds1.interfaces.MesswerteBeanRemote"); } 18.05.17 8:44 20 Johann Mildner, Basel JEE – Business Component Developer Seminar Aufruf einer EJB aus einer anderen EJB des selben EJB Containers Dependency Injection (die zu bevorzugende Art) @EJB BeanInterfaceName bn; Normaler Lookup Context ctx = new InitialContext(); BeanInterfaceName bn= (BeanInterfaceName)context.lookup(LOOKUP_NAME); Übung zu Session Beans (optional) Warenkorb Zu erstellende Artefakte: 1. Warenkorb.java (Interface und Bean) a. einfuegenArtikel(id,bez,preis,stueck) die Artikel werden in eine TreeMap eingefügt b. bestellungAbschliessen es wird eine Lieferschein geschrieben (in Datenbank) – ab dann ist die EJB nicht mehr verfügbar 2. testWarenkorb.jsp Es werden solange Artikel eingefügt, bis die Bestellung abgeschlossen wird. 3. TestWarenkorb.java Ähnlich jsp aber als Massentest von der Konsole aufgerufen. 18.05.17 8:44 21 Johann Mildner, Basel JEE – Business Component Developer Seminar Teil 2 Entity Beans Wir wollen Java Objekte dauerhaft in einer relationalen Datenbank speichern, Datenbanksätze sollen gelesen und in Java Objekten gespeichert werden. Relational nach Objektorientiert und vice versa. JEE bietet diese Möglichkeit über das JPA (Java Persistence API). Vorbereitung Applikationsserver Im Verzeichnis $GLASSFISH_HOME/domains/domain1/lib benötigt es die JAR Datei für die Datenbank. h2-1.3.176.jar postgresql-9.4-1208.jar oracle.jar mysql.jar aus $GLASSFISH_HOME/bin sind folgenede Funktionen aufzurufen (Glassfish muss gestartet sein) H2 ./asadmin create-jdbc-connection-pool --datasourceclassname org.h2.jdbcx.JdbcDataSource h2Pool ./asadmin create-jdbc-resource --connectionpoolid h2Pool jdbc/h2 ORACLE ./asadmin create-jdbc-connection-pool --datasourceclassname oracle.jdbc.pool.OracleDataSource oraclePool ./asadmin create-jdbc-resource --connectionpoolid oraclePool jdbc/oracle POSTGRES ./asadmin create-jdbc-connection-pool --datasourceclassname org.postgresql.ds.PGSimpleDataSource postgresPool ./asadmin create-jdbc-resource --connectionpoolid postgresPool jdbc/postgres 18.05.17 8:44 22 Johann Mildner, Basel JEE – Business Component Developer Seminar in der Server Administration Console http://localhost:4848 ist folgendes durchzuführen H2 Passwort muss gesetzt werden : alter user sa set password 'sa' Resources JDBC JDBC Connection Pools h2Pool Additional Properties url jdbc:h2:tcp://localhost/~/test password sa user sa POSTGRES Resources JDBC JDBC Connection Pools postgresPool Additional Properties url jdbc :postgresql://localhost :5432/jees password jees user jees ORACLE Resources JDBC JDBC Connection Pools oraclePool Additional Properties url jdbc:oracle:thin:@localhost:1521:xe password jees user jees PING muss OK sein !!! 18.05.17 8:44 23 Johann Mildner, Basel JEE – Business Component Developer Seminar Projekt Im Verzeichnis META-INF benötigt es eine Datei persistence.xml Am besten macht man dies über ECLIPSE – Configure/Convert to JPA Project persistence.xml <?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <!-- eclipselink.ddl-generation = none | create-tables | drop-and-create-tables --> <!-- eclipselink.logging.level = all/off/severe/warning/info/config/fine/finer/finest --> <!-- eclipselink.ddl-generation.output-mode = sql-script|database|both --> <persistence-unit name="bcds" transaction-type="JTA"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <jta-data-source>jdbc/h2</jta-data-source> <properties> <property name="eclipselink.ddl-generation" value="drop-and-create-tables" /> <property name="eclipselink.logging.level" value="WARNING" /> <property name="eclipselink.drop-ddl-jdbc-file-name" value="drop.sql" /> <property name="eclipselink.create-ddl-jdbc-file-name" value="create.sql" /> <property name="eclipselink.ddl-generation.output-mode" value="both" /> </properties> </persistence-unit> <!-<jta-data-source>jdbc/h2</jta-data-source> <jta-data-source>jdbc/postgres</jta-data-source> <jta-data-source>jdbc/mysql</jta-data-source> <jta-data-source>jdbc/oracle</jta-data-source> --> </persistence> 18.05.17 8:44 24 Johann Mildner, Basel JEE – Business Component Developer Seminar Entity – Person1 In unserem ersten Beispiel werden wir die Entität Person1 erzeugen. Person1.java package ch.bcds.entities; @Entity public class Person1 implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue Long id; @Column(length = 50) private String name; public Person1() { } public Person1(String name) { this.name = name; } // getter, setter, toString() } Bis auf die Annotationen ist die Klasse Person ein POJO und kann auch als ganz normales Java Objekt benutzt werden. Für den Zugriff auf die Datenbank ist ein Data Access Object (DAO) erforderlich, das unter Zuhilfenahme des EntityManagers das O/R Mapping macht. Das DAO ist eine Stateless Session Bean. 18.05.17 8:44 25 JEE – Business Component Developer Seminar Johann Mildner, Basel Data Access Object – Person1Dao Name des Dienstes: Interface: Implementierung: Methode(n): Person1Dao (Remote, Stateless) Person1DaoBeanRemote Person1DaoBean Long create(String name) Person1 find(Long id) List<?> findAll(); int deleteAll(); Person1DaoBeanRemote.java package ch.bcds2.interfaces; @Remote public interface Person1DaoBeanRemote { Long create(String name); Person1 find(Long id); List<?> findAll(); int deleteAll(); } 18.05.17 8:44 26 Johann Mildner, Basel JEE – Business Component Developer Seminar Person1DaoBean.java package ch.bcds2.beans; @Stateless public class Person1DaoBean implements Person1DaoBeanRemote, Serializable { private static final long serialVersionUID = 1L; @PersistenceContext // (unitName = "bcds") private EntityManager em; @Override public int deleteAll() { return em.createQuery("delete from Person1 p").executeUpdate(); } public Long create(String name) { Person1 p = new Person1(name); em.persist(p); return p.getId(); } public Person1 find(Long id) { return em.find(Person1.class, id); } public List<?> findAll() { Query query = em.createQuery("select p from Person1 p"); return query.getResultList(); } } 18.05.17 8:44 27 Johann Mildner, Basel JEE – Business Component Developer Seminar Test Person1Dao Person1DaoTest.java package ch.bcds2.test; public class Person1DaoTest { static final Logger LOG = Logger.getLogger(Person1DaoTest.class); public static void main(String[] args) throws NamingException { LOG.info("Programmstart"); Person1DaoBeanRemote bean = (Person1DaoBeanRemote) getBean(); // EJB Methoden aufrufen for (int i = 1; i <= 2; i++) { Long id = bean.create(Helper.getName()); Person1 p = bean.find(id); LOG.info(p); } List<?> personen = bean.findAll(); for (Object o : personen) { Person1 person = (Person1) o; LOG.info(person); } LOG.info("Programmende"); } } JPA (Java Persistence API) Siehe separater Lehrgang. 18.05.17 8:44 28 Johann Mildner, Basel JEE – Business Component Developer Seminar Teil 3 Transaktionen CMT – Container Managed Transaction Ohne dass es sichtbar geworden wäre, haben wir bereits im vorigen DB Beispiel Transaktionen verwendet. Sie wurden automatisch vom EJB-Container initiiert und geschlossen Jede Methode einer Bean läuft standardmässig als Transaktion. Über die Annotation @TransactionManager kann man steuern, ob der Container oder die Bean die Transaktion steuert. Standardmässig ist Typ Container eingestellt und der Entwickler muss keine Vorkehrungen treffen. Der Transaktionsschutz bezieht sich nur auf die Datenbank und nicht auf den Zustand der Java Klassen im Hauptspeicher (Heap). Wenn eine Methode einer SessionBean vom Client aufgerufen wird, startet eine Transaktion die alles was der EntityManager macht aufzeichnet. Am Ende der Methode wird ein Commit abgesetzt. Im Fehlerfall ein Rollback. Standardannahme: Jede Methode die von einem Client aufgerufen wird benötigt Transaktionsschutz. Dieses Standardverhalten kann geändert werden Man versieht die Klasse oder ausgewählte Methoden mit einer Annotation. @TransactionAttribute(TransactionAttributeType.TYPE) Mögliche Typen REQUIRED Standardannahme o Die Methode benötigt Transaktionsschutz – ist beim Aufruf schon eine Transaktion aktiv, wird diese genutzt, sonst wird eine neue Transaktion gestartet REQUIRED_NEW o Die Methode benötigt eine neue Transaktion – ist beim Aufruf schon eine Transaktion aktiv, wird diese suspendiert und eine neue Transaktion gestartet. Nach Ende der neuen Transaktion wird die suspendierte Transaktion weitergenutzt. SUPPORTS o Unterstützt Transaktionen, benötigt aber keine. Läuft unter einer ev. bestehenden Transaktion – startet aber keine neue. NOT_SUPPORTED o Eine ev. bestehende Transaktion wird suspendiert und wieder gestartet.. MANDATORY o Transaktionsschutz muss schon beim Aufruf bestehen. NEVER o Es darf kein Transaktionsschutz bestehen. Benötigt man in einer Methode keinen Transaktionsschutz weil man keine Datenbankzugriffe macht oder weil in der Datenbank nur gelesen wird, sollte man NOT_SUPPORTED verwenden. In jedem Fall startet und beendet der Container die Transaktionen. 18.05.17 8:44 29 Johann Mildner, Basel JEE – Business Component Developer Seminar Übung zu CMT Erstellen Sie Hallo2 mit Motto2 Spielen Sie mit sagHallo() und getMotto() herum. Folgende Kombinationen müssen einen Fehler bringen sagHallo() – NEVER getMotto() – MANDATORY sagHallo() – REQUIRED getMotto() – NEVER. 18.05.17 8:44 30 JEE – Business Component Developer Seminar Johann Mildner, Basel BMT – Bean Managed Transaction Man kann die Steuerung von Transaktionen auch der Bean überlassen. Man versieht die Bean Klasse mit einer Annotation. @TransactionManagement(TransactionManagementType.BEAN) Mögliche Typen: CONTAINER BEAN Standardannahme Die Klasse muss Stateful sein und muss in den Besitz einer Instanz der Klasse UserTransaction kommen. Möglichkeiten 1. @Resource UserTransaction ut; // zu bevorzugen! 2. @Resource SessionContext ctx; 3. UserTransaction ut = ctx.getUserTransaction(); InitialContext ctx = new InitialContext(); UserTransaction ut = (UserTransaction) ctx.lookup("java:comp/env/UserTransaction"); Mit dieser Referenz kann man nun die Transaktion selbst steuern @PersistenceContext EntityManager em; @Resource SessionContext ctx; ut.begin(); … em.xxx() … if(fehler) ut.rollback(); else ut.commit(); 18.05.17 8:44 31 Johann Mildner, Basel JEE – Business Component Developer Seminar Person2Dao Ein Programm macht dialoggesteuert einen Insert und anschliessend einen Update. Danach wird entschieden ob ein Commit oder ein Rollback durchgeführt werden soll. Person2.java package ch.bcds3.entities; @Entity public class Person2 implements Serializable { @Id @GeneratedValue Long id; @Column(length = 50) private String name; public Person2() { } public Person2(String name) { this.name = name; } } // getter, setter, toString() Person2DaoBeanRemote.java package ch.bcds3.interfaces; @Remote public interface Person2DaoBeanRemote { Person2 insert(String name); Person2 update(Person2 person); Person2 find(Long id); void start() throws Exception; void stoppCommit() throws Exception; void stoppRollback() throws Exception; void beanDiscard() throws Exception; } 18.05.17 8:44 32 Johann Mildner, Basel JEE – Business Component Developer Seminar Person2DaoBean.java package ch.bcds3.beans; @Stateful @TransactionManagement(TransactionManagementType.BEAN) public class Person2DaoBean implements Person2DaoBeanRemote, Serializable { @PersistenceContext EntityManager em; @Resource UserTransaction ut; @Override public Person2 insert(String name) { Person2 p = new Person2(name); em.persist(p); return p; } @Override public Person2 find(Long id) { return em.find(Person2.class, id); } @Override public Person2 update(Person2 p) { p = em.merge(p); return p; } @Override public void start() throws Exception { ut.begin(); } @Override public void stoppCommit() throws Exception { ut.commit(); } @Override public void stoppRollback() throws Exception { ut.rollback(); } @Override @Remove public void beanDiscard() throws Exception { System.out.println(this.getClass() + " @Remove beanDiscard aufgerufen ..."); } } 18.05.17 8:44 33 Johann Mildner, Basel JEE – Business Component Developer Seminar Person2DaoTest.java package ch.bcds3.test; public class Person2DaoTest1 { static final Logger LOG = Logger.getLogger(Person2DaoTest1.class); public static void main(String args[]) throws Exception { Person2DaoBeanRemote bean = (Person2DaoBeanRemote) getBean(); String name = MyTools.getString("name > "); bean.start(); Person2 p1 = bean.insert(name + "-1"); Person2 p2 = bean.insert(name + "-2"); LOG.info(p1); LOG.info(p2); MyTools.pause(); bean.stoppCommit(); bean.start(); p1.setName(p1.getName() + "-neu1"); bean.update(p1); p2.setName(p2.getName() + "-neu2"); bean.update(p2); LOG.info(p1); LOG.info(p2); MyTools.pause(); String ok = MyTools.getString("ok j/n > "); if (ok.equals("j")) { bean.stoppCommit(); } else { bean.stoppRollback(); } MyTools.pause(); p1 = bean.find(p1.getId()); p2 = bean.find(p2.getId()); LOG.info(p1); LOG.info(p2); } bean.beanDiscard(); } 18.05.17 8:44 34 Johann Mildner, Basel JEE – Business Component Developer Seminar ReiseDao In diesem Programm soll optimistisches Locking gezeigt werden. 1. Es sind 10 Reisen oder Flüge oder Cabinen verfügbar. 2. Es sollen mehrere Threads dialoggesteuert die gleiche Reise auswählen. 3. Der erste Update ist OK. 4. Der zweite Update muss den Client über den konkurrierenden Zugriff benachrichtigen. Reise.java package ch.bcds3.entities; @Entity public class Reise implements Serializable { @Id @GeneratedValue @Column(unique = true, length = 50) @Column(unique = false, length = 50) @Version public Reise() public Reise(String destination) } Long id; private String destination; private String teilnehmer; private int version; {} {this.destination = destination;} // getter, setter, toString() ReiseDaoBeanRemote.java package ch.bcds3.interfaces; @Remote public interface ReiseDaoBeanRemote { List<?> findAll(); List<?> findVerfuegbareReisen(); Reise find(Long id); Reise persist(Reise r); Reise update(Reise r); void init(); void delete(); } 18.05.17 8:44 35 Johann Mildner, Basel JEE – Business Component Developer Seminar ReiseDaoBean.java package ch.bcds3.beans; @Stateless public class ReiseDaoBean implements ReiseDaoBeanRemote { @PersistenceContext // (unitName = "jeesPostgres") private EntityManager em; @Override public List<?> findAll() { Query query = em.createQuery( "select r from Reise r order by r.id"); return query.getResultList(); } @Override public List<?> findVerfuegbareReisen() { Query query = em.createQuery( "select r from Reise r where r.teilnehmer is null order by r.id"); return query.getResultList(); } @Override public Reise update(Reise reise) { reise = em.merge(reise); return reise; } @Override public Reise persist(Reise reise) { em.persist(reise); return reise; } @Override public void delete() { Query q = em.createQuery("delete from Reise r"); int anzahl = q.executeUpdate(); System.out.println("geloescht " + anzahl); } 18.05.17 8:44 36 Johann Mildner, Basel JEE – Business Component Developer Seminar @Override public void init() { Query q = em.createQuery( "select count(r.id) from Reise r where r.teilnehmer is null"); Long anzahl = (Long) q.getSingleResult(); if (anzahl > 0) return; delete(); System.out.println("init aufgerufen !!!"); em.persist(new Reise("London")); em.persist(new Reise("Paris")); em.persist(new Reise("Vaduz")); em.persist(new Reise("Wien")); em.persist(new Reise("Muenchen")); em.persist(new Reise("Budapest")); em.persist(new Reise("Athen")); em.persist(new Reise("Barcelona")); em.persist(new Reise("Lissabon")); em.persist(new Reise("Prag")); } @Override public Reise find(Long id) { Reise reise = em.find(Reise.class, id); return reise; } 18.05.17 8:44 37 Johann Mildner, Basel JEE – Business Component Developer Seminar ReiseDaoTest.java package ch.bcds3.test; public class ReiseDaoTest { static final Logger LOG = Logger.getLogger(ReiseDaoTest.class); public static void main(String args[]) throws Exception { ReiseDaoBeanRemote bean = (ReiseDaoBeanRemote) getBean(); bean.init(); while (true) { List<?> reisen = bean.findVerfuegbareReisen(); for (Object r : reisen) { LOG.info(r); } long id = MyTools.getInteger("waehlen Sie eine Reise aus obiger Liste aus > "); Reise reise = bean.find(id); if (reise == null || reise.getTeilnehmer() != null) { StringBuilder fehlermeldung = new StringBuilder(); fehlermeldung.append("Reise kann nicht gebucht werden: "); fehlermeldung.append(reise == null ? "reise==null" : reise); LOG.info(fehlermeldung.toString()); System.out.println(); } else { String name = MyTools.getString("name > "); reise.setTeilnehmer(name); try { reise = bean.update(reise); LOG.info("Reise gebucht: " + reise); break; } catch (Exception e) { LOG.info("Sie waren zu langsam"); } } } } } 18.05.17 8:44 38 Johann Mildner, Basel JEE – Business Component Developer Seminar Teil 4 Java Message Service Java Message Service (JMS) ist eine Programmierschnittstelle (API) für die Ansteuerung einer Message Oriented Middleware (MOM) zum Senden und Empfangen von Nachrichten aus einem Client heraus, der in der Programmiersprache Java geschrieben ist. JMS hat das Ziel, lose gekoppelte, verlässliche und asynchrone Kommunikation zwischen den Komponenten einer verteilten Anwendung zu ermöglichen. JMS ist Teil der Java Platform, Enterprise Edition. Für die Anwendung braucht man einen Provider, der die API umsetzt und somit den Dienst bereitstellt. Funktionsweise Messaging ist eine Möglichkeit der lose gekoppelten und verteilten Kommunikation in Form von zwischen Softwarekomponenten verschickten Nachrichten. Messaging versucht, die sonst enge Kopplung anderer Kommunikationsmöglichkeiten wie TCP Kommunikation über Sockets, CORBA oder RMI durch die Einführung einer zwischen den Clients gelegenen Komponente aufzubrechen. Damit wird sichergestellt, dass die Clients kein näheres Wissen über die Gegenstelle(n) ihrer Kommunikation haben müssen, was sowohl den Einsatzbereich als auch die Wartung und Wiederverwendung der Komponenten erhöht. JMS und der durch diese angesteuerte Dienst unterstützen zwei unterschiedliche Ansätze zum Versenden von Nachrichten. Zum einen die Nachrichtenwarteschlange (Queue) für sogenannte point-to-point Verbindungen und zum anderen ein Anmelde-Versendesystem (Topic) für Publish-Subscribe-Kommunikation: Queue: Bei der Warteschlange sendet der Sender an eine Queue, an der ein Empfänger hängt. Ist dieser nicht verfügbar, wird die Nachricht gespeichert und der Empfänger kann sie jederzeit später abholen. Man kann dies am besten mit einem Paketdienst vergleichen. Jede Sendung hat genau einen Empfänger. Ist dieser nicht zu Hause, kann er sich die Sendung zu einem beliebigen Zeitpunkt später abholen. Es sind mehrere Empfänger möglich aber nur einer bekommt das „Paket“. Topic: Bei dem Anmelde-Versendesystem werden die Nachrichten an ein Topic geschickt, auf das eine beliebige Anzahl von Empfängern hört. Wird die Nachricht nicht konsumiert, weil kein Empfänger sich an das Topic angemeldet hat, dann ist dies unerheblich. Man kann dies am besten mit einem Radiosender vergleichen (Broadcasting). Entweder man schaltet den Radio ein und hört die Nachricht oder man lässt den Radio ausgeschaltet und hört die Nachricht nicht. 18.05.17 8:44 39 JEE – Business Component Developer Seminar Johann Mildner, Basel Vorbereitung für GLASSFISH4 Applikationsserver Glassfish muss gestartet sein! aus $GLASSFISH_HOME/bin sind folgenede Funktionen aufzurufen CONNECTION_FACTORY ./asadmin create-jms-resource --restype javax.jms.ConnectionFactory jms/jeesConnectionFactory QUEUE und TOPIC ./asadmin create-jms-resource ./asadmin create-jms-resource --restype javax.jms.Queue jms/jeesQueue --restype javax.jms.Topic jms/jeesTopic für MDB’s dürfen die jms-resourcen keine Schrägstriche beinhalten ./asadmin create-jms-resource ./asadmin create-jms-resource --restype javax.jms.Queue jeesQueue --restype javax.jms.Topic jeesTopic Überpruefung localhost:4848 o JMS Resources Connection Factories Destination Resources 18.05.17 8:44 40 Johann Mildner, Basel JEE – Business Component Developer Seminar Implementierung Für die ersten Implementierungen MessageSenden und Message Empfangen sind keine EJB’s erforderlich. Wir senden und empfangen die ersten Messages über Standalone Programme, benötigen aber Glassfish als Messages Service Provider. MessagesConstants.java package ch.bcds4.test; public interface MessagesConstants { final String CF_LOOKUP_NAME = "jeesConnectionFactory"; final String DEST_LOOKUP_NAME = "jeesQueue"; // oder jeesTopic } MessageSenden.java package ch.bcds4.test; public class MessagesSenden implements MessagesConstants { public static void main(String args[]) throws Exception { Context ctx = new InitialContext(); ConnectionFactory cf = (ConnectionFactory) ctx.lookup(CF_LOOKUP_NAME); Connection connection = cf.createConnection(); Destination dest = (Destination) ctx.lookup(DEST_LOOKUP_NAME); Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE); MessageProducer producer = session.createProducer(dest); TextMessage textMessage = session.createTextMessage(); for (int i = 1; i <= 9; i++) { textMessage.setText(i + ". text aus einem Standalone Program"); producer.send(textMessage); } } } producer.close(); 18.05.17 8:44 41 Johann Mildner, Basel JEE – Business Component Developer Seminar MessageEmpfangen.java package ch.bcds4.test; public class MessagesEmpfangen implements MessagesConstants { public static void main(String args[]) throws Exception { LOG.info("Programmstart"); Context ctx = new InitialContext(); ConnectionFactory cf = (ConnectionFactory) ctx.lookup(CF_LOOKUP_NAME); Connection connection = cf.createConnection(); Destination dest = (Destination) ctx.lookup(DEST_LOOKUP_NAME); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageConsumer consumer = session.createConsumer(dest); consumer.setMessageListener(new MessageListener() { @Override public void onMessage(Message message) { TextMessage textMessage = (TextMessage) message; try { } LOG.info(textMessage.getText()); } catch (JMSException e) { e.printStackTrace(); } }); connection.start(); System.out.println("......... Listener mit ENTER beenden ......... "); System.in.read(); } } consumer.close(); session.close(); connection.close(); 18.05.17 8:44 42 Johann Mildner, Basel JEE – Business Component Developer Seminar Message Driven Bean Nachdem nun alle technischen Begriffe und Vorgehensweisen im Zusammenhang mit der Benutzung des JMS betrachtet wurden, ist es an der Zeit, die Frage zu klären, wie diese Mechanismen in EJB’s genutzt werden können. Senden Senden kann man sowohl aus Main Programmen (siehe MessageSenden) als auch aus Beans. SendMessageBeanRemote.java package ch.bcds4.interfaces; @Remote public interface SendMessageBeanRemote { void sendQueueMessage(String text) throws Exception; void sendTopicMessage(String text) throws Exception; } 18.05.17 8:44 43 Johann Mildner, Basel JEE – Business Component Developer Seminar SendMessageBean.java package ch.bcds4.beans; @Stateless public class SendMessageBean implements SendMessageBeanRemote, Serializable { private static final long serialVersionUID = 1L; @Override public void sendTopicMessage(String text) throws Exception { sendMessage(text, "jeesQueue"); } @Override public void sendQueueMessage(String text) throws Exception { sendMessage(text, "jeesTopic"); } private void sendMessage(String text, String destType) throws NamingException, JMSException { Context ctx = new InitialContext(); Destination destination = (Destination) ctx.lookup(destType); ConnectionFactory cf = (ConnectionFactory) ctx .lookup("jms/jeesConnectionFactory"); Connection connection = cf.createConnection(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); MessageProducer messageProducer = session .createProducer(destination); TextMessage message = session.createTextMessage(); message.setText(text); messageProducer.send(message); connection.close(); } } 18.05.17 8:44 44 JEE – Business Component Developer Seminar Johann Mildner, Basel Empfangen Will man Messages in Beans empfangen, geht das nur in MessageDrivenBeans (MDB). Eine MDB kann kurz als zustandsloser, asynchroner Nachrichtenempfänger charakterisiert werden. Bei genauer Betrachtung stellen wir fest, dass hier eine Stateless Session Bean vorliegt, die nur über den Versand von Nachrichten erreichbar ist. MDB’s sind relativ simpel aufgebaut. Sie haben genau eine Business-Methode mit dem Namen onMessage(). Da die Bean simple ist, sind die nötigen Annotationen deutlich umfangreicher als bei anderen Beans. MessageDrivenBean.java (MessageDrivenQueueBean.java, MessageDrivenTopicBean.java) @MessageDriven ( activationConfig= { @ActivationConfigProperty( propertyName="destinationType", propertyValue="javax.jms.Queue"), // od. javax.jms.Topic @ActivationConfigProperty( propertyName="destination", propertyValue="jeesQueue"), // od. jeesTopic @ActivationConfigProperty( propertyName="acknowledgeMode", propertyValue="Auto-acknowledge") } ) public class MessageDrivenXxxxxBean implements MessageListener { @Override public void onMessage(Message message) { TextMessage m=(TextMessage) message; try { } System.out.println(m.getText()); } catch (JMSException e) { e.printStackTrace(); } } 18.05.17 8:44 45 Johann Mildner, Basel JEE – Business Component Developer Seminar Teil 5 Timer Service Kommt immer dann zum Einsatz wenn eine zeitgesteuerte Verarbeitung gewünscht wird. Dabei kann die Anforderung etwas zu tun sein: an einem bestimmten Zeitpunkt oder periodisch alle x Sekunden. Wenn die Methode aufgerufen wird, gibt es keine Verbindung zu einem Client. Es ist lediglich ein Mal ein Anstoss von einem Client nötig. Dann ist es der AS selbst, der den Aufruf steuert. Um einen Timer Service zu implementieren, verwendet man entweder eine Stateless Session Bean oder eine Message Driven Bean Beide ermöglichen es dem AS, sich eine beliebige Beaninstanz aus dem Method-Ready Pool zu entnehmen, um die Timer-Methode aufzurufen. Wir wollen alle 10 Sekunden wissen, wie viel Hauptspeicher dem AS zur Verfügung steht. Dienstname: Interface Bean Methode(n): HauptspeicherUeberwachung HauptspeicherUeberwachungBeanRemote.java HauptspeicherUeberwachungBean.java void startMonitor() void stoppMonitor() long getHauptspeicher() HauptspeicherUeberwachungBeanRemote.java package ch.bcds5.interfaces; @Remote public interface HauptspeicherUeberwachungBeanRemote { void startMonitor(); void stoppMonitor(); long getHauptspeicher() ; } 18.05.17 8:44 46 Johann Mildner, Basel JEE – Business Component Developer Seminar HauptspeicherUeberwachungBean.java package ch.bcds5.beans; @Stateless public class HauptspeicherUeberwachungBean implements HauptspeicherUeberwachungBeanRemote, Serializable { private static final long serialVersionUID = 1L; @Resource private TimerService timerService; String timerName = "HAUPTSPEICHERUEBERWACHUNG"; @Override public void startMonitor() { timerService.createTimer(1 * 1000, 1 * 1000, timerName); } @Override public void stoppMonitor() { for (Object obj : timerService.getTimers()) { Timer timer = (Timer) obj; String tn = (String) timer.getInfo(); } } if (tn.equals(timerName)) { timer.cancel(); System.out.println(tn + ": cancelled"); break; } @Timeout private void timeout(Timer time) { String msg = timerName + ": " + getHauptspeicher(); System.out.println(msg); } } @Override public long getHauptspeicher() { return Runtime.getRuntime().freeMemory(); } 18.05.17 8:44 47 Johann Mildner, Basel JEE – Business Component Developer Seminar HauptspeicherUeberwachungTest.java package ch.bcds5.test; public class HauptspeicherUeberwachungTest { static final Logger LOG = Logger.getLogger(HauptspeicherUeberwachungTest.class); public static void main(String args[]) throws Exception { LOG.info("Programmstart"); HauptspeicherUeberwachungBeanRemote ejb = (HauptspeicherUeberwachungBeanRemote) getBean(); LOG.info("HAUPTSPEICHER: " + String.format("%,d", ejb.getHauptspeicher())); ejb.startMonitor(); Thread.sleep(10000); ejb.stoppMonitor(); LOG.info("HAUPTSPEICHER: " + String.format("%,d", ejb.getHauptspeicher())); LOG.info("Programmende"); } } private static Object getBean() { return Helper.lookup("HauptspeicherUeberwachungBean", "ch.bcds5.interfaces.HauptspeicherUeberwachungBeanRemote"); } 18.05.17 8:44 48 Johann Mildner, Basel JEE – Business Component Developer Seminar Übung1 zu Timer Service Senden Sie die Information über den freien Speicher an das Topic jeesTopic. Passen Sie MessagesEmpfangen an. Es sollten 10 Messages eintreffen. Übung2 zu Timer Service (optional) Stellen Sie sich vor, Sie haben einen Web Shop. Die Clients können Artikel in den Warenkorb stellen. Nach der Auswahl – zur Kassa, wird die Bestellung abgeschlossen. Die abgeschlossenen Bestellungen sollen frühestens nach einer Wartezeit von einem Halben Tag in einem weiteren Prozess abgearbeitet werden. Eine Möglichkeit wäre, dass der Prozess „eine Bestellung abarbeiten“ manuell gestartet wird. Der Typ im Lager nimmt sich jeden Morgen alle noch nicht abgearbeiteten Bestellungen vor und macht sie versandfertig. Eine andere Möglichkeit wäre, immer nach Abschluss einer Bestellung eine Stateless Bean aufzurufen, die einen Timer startet der nach einem Halben Tag (1000*60*60*6) die Abarbeitung der Bestellung startet. 18.05.17 8:44 49 Johann Mildner, Basel JEE – Business Component Developer Seminar Teil 6 Web Services JEE bietet eine einfache Möglichketi Web Services zu erstellen. Dazu ist lediglich eine Remote Stateless Session Bean mit Annotationen (@WebService, @WebMethod …) zu versehen. Service Echo.java package ch.bcds6.webservices; @Stateless @WebService public class Echo { @WebMethod public String echo(@WebParam(name = "name") String name) { return "hallo " + name; } } Client Web Services können Sprachunabhängig genutzt werden. .NETwäre möglich – haben wir aber nicht. Wir verwenden Eclipse. Erstellen Sie ein JavaProjekt bcdsXTestEcho und erstellen Sie folgende Files: 1. generieren Sie in Ihrem Echo Testprojekt die benötigten Files new / other / WebServices / WebServiceClient Server definition : browse http://localhost:8080/EchoService/Echo?wsdl OK FINISH 2. src/ch.bcds.test/TestEchoClient.java EchoServiceClient.java package ch.bcds6.test; public class EchoClient { public static void main(String[] args) throws Throwable { EchoService service = new EchoServiceLocator(); Echo echo = (Echo) service.getEchoPort(); System.out.println(echo.echo("fritz")); } } 18.05.17 8:44 50 Johann Mildner, Basel JEE – Business Component Developer Seminar Teil 7 Life Cycle, Life Cycle Callback Stateless Session Bean, Message Driven Bean Life Cycle NICHT EXISTENT neue Anfrage AKTIVIERT sehr lange inaktiv ZERSTÖRT Life Cycle Callbacks @PostConstruct o Nach der Initialisierung der Bean @PreDestroy o Kurz vor der Zerstörung der Bean Dienstname: Interface: Bean: Methoden: 18.05.17 8:44 LifeCycleSL LifeCycleSLBeanRemote LifeCycleSLBean echo() 51 Johann Mildner, Basel JEE – Business Component Developer Seminar Stateful Session Bean Life Cycle NICHT EXISTENT neue Anfrage AKTIVIERT länger inaktiv oder der Server benötigt Ressourcen PASSIVIERT (auf Festplatte ausgelagert) erneute Anfrage oder sehr lange inaktiv (Timeout) AKTIVIERT sehr lange inaktiv (Timeout) oder @Remove ZERSTÖRT Life Cycle Callbacks Wenn man Ressourcen wie JDBC, Messaging usw. verwendet, dann muss man ggf. zu verschiedenen Zeitpunkten Ressourcen initialisieren oder freigeben. Hierzu verwendet man die Life Cycle Callback Methoden, die automatisch aufgerufen werden, wenn ein bestimmter Zustand erreicht wird. @PostConstruct o Nach der Initialisierung der Bean o (z.B. Datenbank öffnen) @PreDestroy o Kurz vor der Zerstörung der Bean o (z.B. Datenbank schließen) @PrePassivate: o Vor der Passivierung der Bean o (z.B. Datenbank schliessen) @PostActivate: o Nach der Aktivierung der Bean o (z.B. Datenbank öffnen) Dienstname: Interface: Bean: Methoden: 18.05.17 8:44 LifeCycleSF LifeCycleSFBeanRemote LifeCycleSFBean echo() discardBean() mit @Remove 52 JEE – Business Component Developer Seminar Johann Mildner, Basel Entities Life Cycle und Life Cycle Callbacks Am Beispiel Person DOES NOT EXIST Person p = new Person(); NEW em.persist(p) @PrePersist, @PostPersist MANAGED p=em.find() @PostLoad MANAGED Wenn die Entität serialisiert und über das Netz übertragen wird oder wenn em.detach(p) aufgerufen wird DETACHED em.remove(p) @PreRemove, @PostRemove REMOVED em.merge(p) @PreUpdate, @PostUpdate MANAGED Entity: Person3 mit allen 7 Life Cycle Callbacks Dienstname: Interface: Bean: Methoden: 18.05.17 8:44 Person3DaoBean Person3DaoBeanRemote Person3DaoBean nach Bedarf 53 Johann Mildner, Basel JEE – Business Component Developer Seminar Teil 8 Interzeptoren Wenn wir den Aufruf von Methoden protokollieren und eventuell mit einer Zeitmessung versehen wollen, so müssen wir eine Interceptor Klasse schreiben. SimpleInterceptorLogging.java package ch.bcds8.interceptors; public class SimpleInterceptorLogging { @AroundInvoke public Object intercept(InvocationContext context) throws Exception { System.out.println("Logging BEFORE calling method : " + context.getMethod().getName()); Object result = context.proceed(); System.out.println("Logging AFTER calling method : " + context.getMethod().getName()); return result; } } SimpleInterceptorTime.java package ch.bcds8. interceptors; public class SimpleInterceptorTime { @AroundInvoke public Object intercept(InvocationContext context) throws Exception { long startZeit = new Date().getTime(); Object result = context.proceed(); long stoppZeit = new Date().getTime(); long dauer = stoppZeit - startZeit; System.out.println("Duration : " + dauer + " Millisekunden " + context.getMethod().getName()); } return result; } 18.05.17 8:44 54 Johann Mildner, Basel JEE – Business Component Developer Seminar Die Klasse deren Methoden protokolliert werden soll, bekommt die Annotation @Interceptors. Mehr ist nicht zu tun. Die Methoden in dieser Klasse wissen nichts von einer Protokollierung. SimpleInterceptorBeanRemote.java package ch.bcds8.interfaces; @Remote public interface SimpleInterceptorBeanRemote { String printMessage(String m); } SimpleInterceptorBean.java package ch.bcds8.beans; @Stateless @Interceptors ({ SimpleInterceptorLogging.class, SimpleInterceptorTime.class }) public class SimpleInterceptorBean extends SimpleInterceptorBeanRemote { public String printMessage(String m) { System.out.println("Executing method: printMessage " + m); return "Message is " + m; } } 18.05.17 8:44 55 Johann Mildner, Basel JEE – Business Component Developer Seminar SimpleInterceptorTest.java package ch.bcds8.test; public class SimpleInterceptorTest { static final Logger LOG = Logger.getLogger(SimpleInterceptorTest.class); public static void main(String args[]) throws Exception { LOG.info("Programmstart"); SimpleInterceptorBeanRemote ejb = (SimpleInterceptorBeanRemote) getBean(); System.out.println(ejb.printMessage("message aus siTest")); } } LOG.info("Programmende"); private static Object getBean() { return Helper.lookup("SimpleInterceptorBean", "ch.bcds8.interfaces.SimpleInterceptorBeanRemote"); } 18.05.17 8:44 56 JEE – Business Component Developer Seminar Johann Mildner, Basel Teil 9 Was noch nicht behandelt wurde LocalBeans Wenn eine Bean kein Interface hat – no interface Bean so ist sie eine LocalBean. Die Bean hat entweder keine Annotation bezüglich Local oder die Annotation @LocalBean. Eine lokale Bean kann nicht remote aufgerufen werden. (Servlet oder JSP). @EJB BeanName bean; Uebung: NoInterfaceBean mit der Methode String echo(String s) NoInterfaceServlet Beans mit asynchronen Methoden müssen void sein oder ein Future Object zurückliefern. Future liefern geht nur wenn der Client im gleichen AS läuft z.B. in einem Servlet oder einem BackingBean die Bean mit den AsyncMethoden muss Stateless sein. Wenn nicht, muss der Client zwar nicht warten aber ... die Methoden laufen nicht parallel sondern wartet bis keine andere Methode mehr läuft (Synchronisation ?) Uebung: Name des Dienstes: Interface: Implementierung: Methode(n): AsynchroneMethoden (Stateless) AsynchroneMethodenBeanRemote.java AsynchroneMethodenBeanjava void syncEcho (String s) gibt s und eine Zufallszahl aus void asyncEcho1 (String s) gibt s und eine Zufallszahl aus Future asyncEcho2 (String s) liefert s mit einer Zufallszahl Damit man die Asynchronität beim Test auch sieht, soll in den Methoden sleep(10000) aufgerufen werden und Anfang und Ende der Methoden protokolliert werden. 18.05.17 8:44 57