DiskStore EJB client app, log4j config, custom context-root Óbudai Egyetem, Java Enterprise Edition Műszaki Informatika szak Labor 5 Bedők Dávid 2016.01.25. v0.2
Remote vs. Local EJB Call 2
Erőforrások Mi kell ahhoz, hogy egy EJB service-t meghívjunk? Ismernünk kell a service futásának helyét (gép, port) Ismernünk kell a service kiszolgáló specifikus körülményeit (application server, jndi rules,...) Ismernünk kell a meghívandó service aláírását, és rendelkeznünk kell azokkal az osztályokkal, melyek mindehhez szükségesek (paraméterek, visszatérési érték, kivételek) Mindez a csomagolás átszervezését/átgondolását fogja jelenteni! 3
Technikai követelmények JBoss specifikus JEE 6 library használata az alap JEE 6 library helyett (container specifikus viselkedés végett) Korábban a fordításhoz használhattuk a standard JEE 6 library-t, futás közben a container a JBoss specifikusat helyezte el a classpath-on A kliens kód viszont fontos, hogy a specifikussal fusson, hiszen az egy külön JVM-ben lesz a containertől. A Remote hívás során a kommunikációban részt vevő osztályok példányai hálózaton keresztül közlekednek, melyekhez ezek serializálása/deserializálása szükséges. Az érintett osztályokat erre fel kell tudni készíteni (implements Serializable) 4
Lemezbolt Feladat: azonos módon a BookStore -hoz hozzunk létre egy lemezboltot, melyben ID alapján egy lemez, illetve az összes lemez lekérdezhető egy EJB service-en keresztül. Nem kell persistence réteg, adjunk vissza teszt lemez adatokat helyette. Készítsünk egy weblayer-t is, mely egyetlen servlet segítségével teszteli a lokális EJB hívást is (DiskPingServlet). 5
Project struktúra diskstore (root project) ds-client (EJB client alkalmazás) ds-ejbserviceclient (közös erőforrások, Stubok, kivételek, remote interface) ds-ejbservice (EJB service alkalmazás) ds-weblayer (DiskPingServlet, local EJB client) Része az EAR-nak: sárga és zöld project-ek. Része az EJB client classpath-nak: kék és zöld project. 6
EJB Service client project hu.qwaevisz.diskstore.ejbserviceclient DiskFacadeRemote.java hu.qwaevisz.diskstore.ejbserviceclient.domain DiskStub.java hu.qwaevisz.diskstore.ejbserviceclient.exception ServiceException.java 7
EJB Service Client Közös erőforrás build.gradle jar { archivename 'ds-ejbservice-client.jar' dependencies { compile group: 'org.jboss.spec', name: 'jboss-javaee-6.0', version: jbossjee6version JBoss specifikus JEE6 library (jbossjee6version: '3.0.3.Final') 8
Remote interface DiskFacadeRemote.java package hu.qwaevisz.diskstore.ejbserviceclient; import javax.ejb.remote; import hu.qwaevisz.diskstore.ejbserviceclient.domain.diskstub; import hu.qwaevisz.diskstore.ejbserviceclient.exception. ServiceException; @Remote annotáció a különbség a local @Remote interface-hez képest. public interface DiskFacadeRemote { DiskStub getdisk(int diskid) throws ServiceException; 9
Stub DiskStub.java package hu.qwaevisz.diskstore.ejbserviceclient.domain; import java.io.serializable; public class DiskStub implements Serializable { private static final long serialversionuid = -1; private int diskid; private String author; private String title; private double price;... implements Serializable nagyon lényeges! 10
Exception ServiceException.java package hu.qwaevisz.diskstore.ejbserviceclient.exception; public class ServiceException extends Exception { private static final long serialversionuid = -1; public ServiceException(String message) { super(message); public ServiceException(String message, Throwable cause) { super(message, cause); Az Exception alapból implements Serializable, melyet itt ki is használunk. 11
Webproject context root Webalkalmazás context root-jának beállítása: web.xml WAR: WEB-INF/web.xml Ha a webproject önállóan deployment egy alkalmazás serverben/webcontainer-ben application.xml EAR: META-INF/application.xml Ha a webproject egy EAR webmodule-jának részeként deployolódik egy alkalmazás szerverben. Gradle EAR plugin-je a war file nevét adja context-rootnak alapértelmezésben. 12
Custom context-root (EAR plugin) ext {... ear {...... webapplicationname = 'ds-weblayer.war' webapplicationcontextpath = 'diskstore' deploymentdescriptor { webmodule( webapplicationname, webapplicationcontextpath ) root project: build.gradle diskstore lesz az egyéni context-root!... war { archivename webapplicationname weblayer project: build.gradle 13
EJB Client project jar { archivename 'ds-client.jar' build.gradle repositories { flatdir { dirs 'lib' dependencies { compile group: 'org.jboss.spec', name: 'jboss-javaee-6.0', version: jbossjee6version compile project(':ds-ejbserviceclient') compile name: 'jboss-client', ext: 'jar' JBoss client alkalmazáshoz szükséges a JBoss client jar. Ez megtalálható: \jboss-eap-6.4\bin\client\jboss-client.jar Ha saját gépről szeretnénk library-t a project függőségei közé felvenni, akkor egy flatdir repository-t kell defininálni. Ebben is keresni fogja a függőségeket a gradle. A lib könyvtárnak a project gyökerében kell lennie. a jjboss-client.jar log4j appender-t is tartalmaz, így egyszerűen naplózható a kliens oldal 14
Kliens osztály I. package hu.qwaevisz.diskstore.client;... public class DiskClient { InitialContext osztálya DiskClient.java private static final String JBOSS_INITIAL_CONTEXT_FACTORY = " org.jboss. naming.remote.client.initialcontextfactory "; private static final String JBOSS_PROVIDER_URL = " remote://localhost: 4447"; private static final String JBOSS_URL_PKG_PREFIXES = " org.jboss.ejb. host:port client.naming "; JBoss specifikus szabályok private static final String JBOSS_NAMING_CLIENT_EJB_CONTEXT_KEY = " jboss. naming.client.ejb.context "; private static final String JBOSS_NAMING_CLIENT_EJB_CONTEXT_VALUE = "true"; public static void main(string[] args) throws Exception { System.out.println( new DiskClient().invoke(1001)); 15
Kliens osztály II. DiskClient.java private DiskFacadeRemote lookup() throws NamingException { final Hashtable<String, String> jndiproperties = new Hashtable<String, String>(); jndiproperties.put(context.initial_context_factory, JBOSS_INITIAL_CONTEXT_FACTORY); jndiproperties.put(context.provider_url, JBOSS_PROVIDER_URL); jndiproperties.put(context.url_pkg_prefixes, JBOSS_URL_PKG_PREFIXES); jndiproperties.put(jboss_naming_client_ejb_context_key, JBOSS_NAMING_CLIENT_EJB_CONTEXT_VALUE); final Context context = new InitialContext(jndiProperties); return (DiskFacadeRemote) context.lookup( "diskstore/dsejbservice/diskfacadeimpl!hu.qwaevisz.diskstore.ejbserviceclient. DiskFacadeRemote"); EJB Service Global JNDI neve! Deployment során látszik a server.log-ban, illetve management console-ról és JBoss CLIon keresztül is lekérdezhető. 16
Kliens osztály III. private DiskStub invoke(int diskid) { DiskStub disk = null; try { final DiskFacadeRemote facade = this.lookup(); disk = facade.getdisk(diskid); catch (final ServiceException e) { e.printstacktrace(); catch (final NamingException e) { e.printstacktrace(); return disk; DiskClient.java Remote interface-en keresztül meghívjuk az igényelt szolgáltatást (type-safe megoldás). 17
Kliens oldali naplózás log4j.xml elhelyezése a classpath-on pl. src/main/resources Utána gradle eclipse lefuttatása, vagy eclipse-ben az adott könyvtár hozzáadása a source könyvtárak listájához (project properties Java Build Path Source tab) 18
Kliens naplózás konfigurációja log4j.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration debug="true" xmlns:log4j='http://jakarta.apache.org/log4j/'> <appender name=" console" class="org.apache.log4j.consoleappender"> <layout class="org.apache.log4j.patternlayout"> <param name="conversionpattern" value="%d{yyyy-mm-dd HH:mm:ss %-5p %c{1:%l - %m%n" /> </layout> </appender> <root> <level value=" DEBUG" /> <appender-ref ref=" console" /> </root> </log4j:configuration> Definiálunk egy console appendert, mely egy ConsoleAppender, és hozzáadjuk DEBUG szinten a root logger-hez (mely mindig létezik). 19