inhoudstafel en auteursrecht
* STER *
Bedrijfstoepassingen moeten schaalbaar zijn. Dat wil zeggen dat ze met het bedrijf mee moeten groeien, liefst zonder compleet te worden herschreven. Neem bijvoorbeeld de boekhouding van een kruidenierszaak. Aanvankelijk gebeurt die op één enkel PC-tje in de winkel, dat tevens als kassa dienstdoet. Op een bepaald moment transformeert de zaak zich in een minimarkt. De twee kassa's worden natuurlijk gekoppeld aan de boekhouding, die inmiddels naar het kantoortje achterin is verhuisd: lokaal netwerk. De minimarkt is succesvol, koopt een paar concurrenten, en voor je het weet is een supermarktketen geboren: wide area networking met redundante centrale servers, enzovoort.
In het voorbeeld hierboven zien we dat Java van nature enkele sterke troeven in handen houdt. Met name de platform-onafhankelijkheid maakt het mogelijk, één ontwikkelings- en uitbatingsplatform te hanteren vanaf de kleinste netwerkmachientjes (zoals kassa's) tot de krachtigste centrale mainframe.
Een belangrijk aspect van de schaalbaarheid is gedistribueerd programmeren, dat wil zeggen dat verschillende componenten van het informatiesysteem op verschillende machines draaien. Die machines (dus de componenten van het systeem) moeten met elkaar praten.
Communicatie tussen verschillende machines is in Java op meer dan één manier mogelijk. De meest natuurlijke aanpak is Remote Method Invocation of kortweg RMI. Door RMI kan een Java-programma op één machine een methode aanroepen van een klasse die in het geheugen van een andere machine zit. Programmeertechnisch is het niet zo moeilijk: het volstaat dat de klasse die aangeroepen wordt, een bepaalde interface implementeert. Om de twee programma's te doen draaien, is nog een beetje configuratiewerk op de twee machines nodig. Er moet ook wat extra code geschreven worden om een object van de "server"-klasse te creëren en aan te melden (zodat andere programma's haar methoden kunnen aanroepen). Vooral dit laatste kan, bij een complex informatiesysteem met een groot aantal RMI-klassen, aanleiding geven tot moeilijk onderhoudbare code.
Om programmeurs af te schermen van de techniciteit van RMI, én om toe te laten dat dezelfde methode zowel lokaal als gedistribueerd wordt opgeroepen (schaalbaarheid!) ontwikkelde Sun het begrip Enterprise JavaBean of EJB. Een EJB is een JavaBean (zie hoofdstuk 8) die speciaal gemaakt is om via RMI te worden aangeroepen. Een EJB kan ook zelf via RMI andere EJBs aanroepen. EJBs worden automatisch gecreëerd zonder dat de programmeur zelf nog opstart- of aanmeldcode moet schrijven.
Sun creëerde EJBs samen met een aantal andere technieken (JSP, JNDI, Connector Architecture) en biedt het geheel aan onder de naam Java 2 Enterprise Environment (J2EE). EJBs de kern van de bundel. Door hun nauwe integratie met J2EE komen EJBs vaak voor in toepassingen die de andere aspecten van J2EE gebruiken, bijvoorbeeld in webapplicaties. In het algemeen zijn EJBs populair als een systeem uit verschillende software-lagen bestaat, bijvoorbeeld database - business logic - grafische presentatie.
EJBs hebben slechts één nadeel ten opzichte van "gewone" Java-klassen: ze gebruiken nogal wat geheugen en processor-capaciteit. De traditionele voorsprong van Java-programma's ten opzichte van visuele "kant-en-klaar" omgevingen gaat hierdoor gedeeltelijk verloren.
De volgende tabel vat samen wanneer het gebruik van EJBs aan te bevelen is bij de ontwikkeling van een nieuw informatiesysteem.
| Systeemaspect | Gebruik géén EJBs als... | Gebruik wél EJBs als... |
|---|---|---|
| Hardware/Architectuur | het systeem per definitie nooit over verscheidene machines kan gespreid worden (bv. systeemprogramma) | er een redelijke hoop bestaat dat het systeem binnen vijf jaar te groot is voor één machine (bv. beheer aandelenportefeuilles) |
| Hardware | geheugen en processortijd uiterst beperkt of kostbaar zijn (bv. actiespelletje) | de machines goedkoper zijn dan de programmeurs (bv. kassa) |
| Businessmodel | het systeem moet verkocht - of geschonken - worden aan een groot aantal particulieren (bv. applet) | het systeem voor professioneel gebruik bestemd is (bv. boekhouding) |
| Software-architectuur | het systeem een alleenstaande utility is die geen gebruik maakt van een database en die niet is uitgerust met een grafische gebruikersinterface (bv. batchprogramma) | het systeem van nature uit verschillende lagen bestaat (bv. data mining) |
| Interactie | het systeem op zichzelf staat (bv. rekenmachine) | het systeem moet interageren met bestaande software, al dan niet op dezelfde machines (bv. reservering van hotelkamers) |
Om een EJB te ontwikkelen, moet de programmeur niet minder dan drie bronbestanden leveren:
Een EJB is in de eerste plaats een dienstverlenend object. Het ontleent zijn nut
aan het feit dat andere Java-code zijn methoden kan aanroepen. De client interface
(ook bean interface of remote interface genoemd)
is een gewone Java-interface (zie paragraaf 4.8) waarin de signatuur van de
extern bruikbare methoden wordt gespecificeerd. De client-interface moet een extensie
zijn van de gegeven interface javax.ejb.EJBObject. Deze laatste is
op zijn beurt een extensie van
de interface java.rmi.Remote, een EJB is dus een remote object in de
betekenis van Remote Method Invocation. Dat betekent ondermeer dat alle methoden
van de client interface een RemoteException kunnen veroorzaken.
De home interface is ook een Java-interface, maar de methoden die erin
voorkomen zijn geen methoden van de EJB zelf. Het zijn factory-methoden
die de client in staat stellen een object van het gewenste EJB-type terug te
vinden of te creëren. De home interface is een uitbreiding van de standaard-interface
javax.ejb.EJBHome.
De implementatie van de bean is niets anders dan de klassendefinitie.
Deze klasse zal de implementatie leveren van de nodige functionaliteit om de
twee hogergenoemde interfaces in te vullen. De enterprise javabean
implementeert de interface javax.ejb.EnterpriseBean, meestal
via een van de drie subinterfaces die de drie standaardtypes EJB vertolken:
javax.ejb.SessionBean
javax.ejb.EntityBean
javax.ejb.MessageDrivenBean
implements (zie paragraaf 4.8). De functionaliteit
van de methoden van beide interfaces wordt geleverd door bean-methoden met lichtjes
verschillende namen. Het uitbatingssysteem ('bean container') legt het
verband.We construeren een demonstratiebean die als "dienstverlening" niet meer doet dan een eenvoudige groet opsturen in de vorm van een constante tekststreng. De client interface ziet er als volgt uit.
import java.rmi.RemoteException;
import javax.ejb.EJBObject;
public interface Groet extends EJBObject {
public String zegHet() throws RemoteException;
}
De home interface bevat niet meer dan een methode om een object van het type Groet
aan te maken.
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
public interface GroetHome extends EJBHome {
Groet create() throws RemoteException, CreateException;
}
De uitzondering javax.ejb.CreateException signaleert een algemeen probleem bij het
aanmaken van een object van het type Groet.
De implementatie van onze bean gaat via de interface SessionBean.
import java.rmi.RemoteException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
public class GroetBean implements SessionBean {
public String zegHet() {
return "Hallo, EJB!";
}
public void ejbCreate() {}
public void ejbRemove() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void setSessionContext(SessionContext sc) {}
}
De methode zeghet komt overeen met de gelijknamige methode in de remote interface. De methode
ejbCreate is nodig omdat we een create-methode hebben in de home interface. De andere
vier methoden worden opgelegd door de interface SessionBean.
De drie bovenstaande bronbestanden volstaan niet om een enterprise javabean te definiëren. We hebben ook
een XML-bestand nodig dat vastlegt hoe deze drie bestanden samenwerken. Dit XML-bestand is de deployment
descriptor. In de volgende paragraaf geven we aan hoe de deployment descriptor van
Groet er zou kunnen uitzien.
Om de drie bronbestanden te compileren, moet je de compiler vertellen waar hij de definitie van klassen zoals
javax.ejb.SessionBean enz. kan vinden; die zitten namelijk niet in de standaard JDK. In de
volgende paragraaf installeren we de applicatieserver JBoss; als onderdeel van die installatie zullen we
over de nodige jarfiles beschikken waarin de J2EE-klassen zitten opgeslagen. We stellen de compilatie uit
tot dan.
In voorbereiding:
De Bean Container of EJB Container is een computerprogramma dat de EJB-productie-omgeving creëert.
Dat wil zeggen dat dit programma permanent moet draaien op minstens één server die voor de gebruikers
toegankelijk is. De EJB-container leest de configuratiebestanden voor de verschillende enterprise javabeans, en
creëert deze beans naargelang de behoefte. Hij is tevens verantwoordelijk voor het bewaren en terug oproepen
van beans met persistente attributen, voor het consistente beheer van transacties, en voor het gecentraliseerde
beheer van gedeelde hulpbronnen (resource pooling). Op de softwaremarkt worden bean containers vaak verpakt
in een geheel samen met een webserver, een naamserver, een JSP-server enzovoort;
dat geheel heet dan een application server.
Op het moment dat we dit schrijven, is de markt voor application servers nog niet helemaal tot rust gekomen. Zeker is
dat de Open Source-beweging hier een sterke positie heeft. In deze tekst kiezen we voor het Open Source-product
JBoss. Volgens voorstanders is deze application server de meest robuuste (minst foutvrije) van het zootje,
en is hij bovendien relatief eenvoudig te beheren.
Je kan JBoss downloaden vanaf de site http://www.jboss.org. Ga op zoek naar "downloads",
vervolgens naar "JBoss Application Server". Op het moment dat we dit schrijven, is versie 4.0.0 de meest recente
stabiele release. Hij wordt aangeboden op de download-site SourceForge
(http://sourceforge.net) in diverse compressieformaten. Je kan in principe
eender welk formaat downloaden waarvoor je het nodige decomprimeerprogramma bezit, en je hebt ook de keuze tussen een
source release (met broncode, herkenbaar aan de letters src in de bestandsnaam)
of een binary release (alleen de installatieset). Wij opteren in
dit voorbeeld voor het ZIP-archief met de naam jboss-4.0.0.zip
Bewaar het archief op een bekende plaats. Extraheer zijn inhoud naar een map waarvan de volledige padnaam geen spaties bevat
Goede voorbeelden zijn: de root-map c:\ of de map c:\dev; een slecht voorbeeld is:
c:\program files\jboss (want dat bevat een spatie). Wij creëren eerst een map c:\dev en
extraheren de inhoud van het zip-archief daarheen.
Op het bovenste niveau van het ZIP-archief staat een map met de naam jboss-4.0.0 (of in jouw geval, iets gelijkaardigs
met een ander versienummer).
JBoss heeft een Java-omgeving nodig om te werken. Minimaal is dat een Java Runtime Environment (een virtuele machine), maar om
JSP's te draaien heb je ook een compiler nodig. JBoss 4.0.0 heeft minimaal Java versie 1.4 nodig. Wij hebben
een complete JDK 1.5 geïnstalleerd in de map c:\dev\jdk1.5.0 We moeten aan JBoss laten weten waar hij
de JRE kan vinden door de systeemveranderlijke JAVA_HOME een waarde te geven die gelijk is aan de padnaam
van de top van de Java-installatie. Het hangt af van je beheerssysteem hoe je systeemveranderlijken kan creëren;
in Microsoft Windows XP gaat dit via de utility Systeem op het Configuratiepaneel (Control Panel/System).
Druk in het tabblad "Geavanceerd" (Advanced) op de knop "Omgevingsveranderlijken" (Environment Variables)
en creëer in de onderste helft van het dialoogvenster een nieuwe veranderlijke met de naam JAVA_HOME en als waarde
de padnaam van je JDK-installatie.
Test je installatie als volgt. Open een opdrachtvenster ("Command Prompt" in MS Windows). Test of de omgevingsveranderlijke de juiste waarde heeft door te typen
echo %JAVA_HOME%
Het systeem moet nu antwoorden met de padnaam van je JDK-installatie. Ga vervolgens naar de deelmap bin van
de installatiemap van JBoss, en start de server met het commando run.
cd \dev\jboss-4.0.0\bin run
Als alles goed gaat, moet JBoss nu starten. Je ziet in het commandovenster een lange reeks technische boodschappen verschijnen, met op het einde een melding van de duurtijd van de opstartprocedure.
Controleer dat de webserver van JBoss effectief bereikbaar is door in een browser het adres http://localhost:8080 aan te roepen.
Je kan JBoss terug uitschakelen door in hetzelfde commandovenster de loop van het programma te onderbreken (in MS-DOS: met de toetscombinatie Ctrl-C).
We zullen nu de EJB Groet van de vorige paragraaf ontplooien in JBoss. In elk geval hebben
we een deployment descriptor nodig. Dat is een XML-bestand dat ondermeer aangeeft welke Java-bestanden
samen een EJB uitmaken. De deployment descriptor van Groet draagt de naam
ejb-jar.xml en ziet er als volgt uit.
<?xml version="1.0"?> <!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN' 'http://java.sun.com/dtd/ejb-jar_2_0.dtd'> <ejb-jar> <description>Eenvoudigste EJB-applicatie</description> <display-name>Hallo, EJB demonstratietoepassing</display-name> <enterprise-beans> <session> <display-name>Hallo, EJB demonstratiebean</display-name> <ejb-name>GroetBean</ejb-name> <home>GroetHome</home> <remote>Groet</remote> <ejb-class>GroetBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> </session> </enterprise-beans> </ejb-jar>
We moesten onze drie Java-bronbestanden ook nog compileren. Als onderdeel van de JBoss-installatie vind je
een map server...all...lib met daarin ondermeer het archief jboss-j2ee.jar. Neem die
jarfile op in het classpath via de opdrachtregel van de compiler.
Bij ons is JBoss geïnstalleerd in de map
c:\dev\jboss-4.0.0, dus we compileren met
javac -cp c:\dev\jboss-4.0.0\server\all\lib\jboss-j2ee.jar;. *.java
De definitie van onze bean bestaat nu al uit vier verschillende bestanden. Het geheel
kan best aan JBoss worden aangeboden in de vorm van een Java-archief (jarfile, zie
hoofdstuk 5b). De interne structuur van dat archief, dat we hier voorbeeld.jar
noemen, is de volgende. De deployment descriptor gaat in de deelmap META-INF, die reeds het manifest bevat.
De gecompileerde klassenbestanden GroetBean.class, Groet.class en GroetHome.class
komen op het hoogste niveau.
voorbeeld.jar:
|
----------- META-INF
| |
| -------------- ejb-jar.xml
|
----------- Groet.class
|
----------- GroetBean.class
|
----------- GroetHome.class
Hier is een stappenplan voor de creatie van het archief:
META-INF op de plaats waar zich de klassenbestanden bevinden;jar cvf voorbeeld.jar Groet.class GroetBean.class GroetHome.class META-INF
Inspecteer de inhoud van de jarfile met een ZIP-tool, bijvoorbeeld 7Zip. Verplaats vervolgens de jarfile naar de map
C:\dev\jboss-4.0.0\server\default\deploy (of een gelijkaardige plaats, afhankelijk van waar je JBoss hebt
geïnstalleerd). De bean wordt nu automatisch ontplooid, zonder dat je de server moet herstarten! In het opdrachtvenster
verschijnt een boodschap in de volgende trant:
22:44:28,156 INFO [EjbModule] Deploying GroetBean 22:44:28,218 INFO [EJBDeployer] Deployed: file:/C:/dev/jboss-4.0.0/server/default/deploy/voorbeeld.jar
Je kunt de aanwezigheid van de nieuwe bean ook via de management console van JBoss waarnemen: ga naar de
webpagina http://localhost:8080/jmx-console/ en zoek naar het optreden van de tekst "Groet"
(via de zoekfunctie in de pagina, bij MS Internet Explorer is dit Ctrl-F).
De beste controle of onze bean correct ontplooid is en goed werkt, is hem gebruiken in een clientprogramma. Voor de gelegenheid gebruiken we een heel eenvoudige client, gelanceerd vanaf de opdrachtregel. In praktische situaties zullen EJBs vaak worden aangesproken vanuit Java-objecten die eveneens op een applicatieserver draaien, bijvoorbeeld JSP-pagina's. Hier is alvast de code van onze voorbeeldclient.
import javax.naming.*;
import javax.rmi.PortableRemoteObject;
/** Eenvoudige client om aan te tonen dat de EJB GroetBean
* ontplooid is en werkt. De opdrachtregel moet de volgende
* parameters specificeren:
* java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
* java.naming.provider.url=localhost:1099
*/
public class GroetClient {
/** Creëer een instantie van GroetBean en vang haar boodschap op. */
public static void main(String[] args) {
try {
InitialContext jndiContext = new InitialContext();
Object ref = jndiContext.lookup("GroetBean");
GroetHome home = (GroetHome)
PortableRemoteObject.narrow (ref, GroetHome.class);
Groet deBean = home.create();
System.out.println("Boodschap van Groet-bean luidt:");
System.out.println(deBean.zegHet());
}
catch(Exception e) {
System.out.println(e.toString());
}
}
}
Vergelijk dit programma met het voorbeeld van paragraaf 6b.3. Een EJB container heeft altijd een
naming service, en elke Enterprise JavaBean word automatisch bij die service geregistreerd als onderdeel
van zijn normale deployment. We krijgen dus van de container een referentie naar de gezochte bean, die
we opslaan in de veranderlijke ref.
De naming context factory van de JBoss naming service zit in de klasse
org.jnp.interfaces.NamingContextFactory (jnp staat voor JBoss Nameservice Provider).
De naming service wordt aangesproken via een speciaal toepassingsprotocol bovenop IP, dat standaard gebruik maakt
van poort nummer 1099. De naam waaronder een EJB geregistreerd staat, komt overeen met het element
<ejb-name> in zijn deployment descriptor.
Om de broncode van GroetClient te compileren, volstaat het dat de compiler toegang heeft tot de
gewone J2EE-klassen. Compileer bijvoorbeeld zoals hierboven met de opdrachtregel
javac -cp c:\dev\jboss-4.0.0\server\all\lib\jboss-j2ee.jar;. GroetClient.java
Om de client te starten moeten we de twee hogergenoemde parameters invullen met de juiste informatie over de naming service. De opdrachtregel zal er ongeveer als volgt uitzien (we splitsen over verschillende regels voor de leesbaarheid, typ dit als één enkele regel):
java -cp c:\dev\jboss-4.0.0\client\jbossall-client.jar;. -Djava.naming.factory.initial=org.jnp.interfaces.NamingContextFactory -Djava.naming.provider.url=localhost:1099 GroetClient
Als je tevoren niet vergeten bent JBoss te starten en de bean te deployen, dan zou het volgende resultaat moeten verschijnen:
Boodschap van Groet-bean luidt: Hallo, EJB!
Als de client om één of andere reden geen contact krijgt met JBoss, dan zie je eerder iets in de volgende trant.
javax.naming.CommunicationException: Receive timed out [Root exception is java.net.SocketTimeoutException: Receive timed out]
In voorbereiding: container managed persistence (CMP) en bean managed persistence (BMP)