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 interface-hez képest. public interface DiskFacadeRemote { @Remote
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;
implements Serializable nagyon lényeges!
private String title; private double price; ... } 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); }
Az Exception alapból implements Serializable , melyet itt ki is használunk.
public ServiceException(String message, Throwable cause) { super(message, cause); } }
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 {
root project: build.gradle
... webapplicationName = 'ds-weblayer.war' webapplicationContextPath = 'diskstore'
diskstore lesz az egyéni context-root!
} ... ear { deploymentDescriptor { webModule( webapplicationName, webapplicationContextPath ) } } ...
weblayer project: build.gradle ... war { archiveName webapplicationName } 13
EJB Client project build.gradle jar { archiveName 'ds-client.jar' } repositories { flatDir { dirs 'lib' } }
dependencies {
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.
compile group: 'org.jboss.spec', name: 'jboss-javaee-6.0', version: jbossjee6Version compile project(':ds-ejbserviceclient') compile name: 'jboss-client', ext: 'jar' } a jjboss-client.jar log4j appender-t is tartalmaz, így egyszerűen naplózható a kliens oldal
14
Kliens osztály I. DiskClient.java package hu.qwaevisz.diskstore.client; ... public class DiskClient {
InitialContext osztálya
private static final String JBOSS_INITIAL_CONTEXT_FACTORY = " org.jboss. naming.remote.client.InitialContextFactory "; private static final String JBOSS_PROVIDER_URL = " remote://localhost: 4447"; host:port private static final String JBOSS_URL_PKG_PREFIXES = " org.jboss.ejb. JBoss specifikus “szabályok” client.naming "; 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 = String>();
new Hashtable<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. DiskClient.java 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; }
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
<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n" />
Definiálunk egy “console” appendert, mely egy “ConsoleAppender”, és hozzáadjuk DEBUG szinten a root logger-hez (mely mindig létezik).
19