EJB - Pavut pähkinänkuoressa

Mureakuha

Loikkaa: valikkoon, hakuun

Sisällysluettelo

EJB - Enterprise JavaBeans by Japppppp

Pavut pähkinänkuoressa, tai pikemminkin hillopurkissa.


EJB on käsite, joka on nostanut viimeaikoina päätään yhä enemmän esiin ohjelmointipiireissä. Varsinkin isot ohjelmistotalot kehuvat papuja taivaisiin, eikä suotta, onhan siitä tulossa eräs suosituimmista hajautettujen järjestelmien toteuttamistavoista. EJB:stä voisi kirjoittaa pidemmänkin tutoriaalin mutta tämä opas valottaa EJB:n käsitteistöä sekä paneutuu kyseisen arkkitehtuurin perusteisiin antaen kuitenkin valmiudet luoda pienehköjä applikaatioita. Oletan kuitenkin, että osaat Java-kielen ja Java-ohjelmoinnin perusteet sekä tiedät miten asentaa J2EE (Enterprise Edition). J2EE kannattaakin asentaa välittömästi, sen saa ilmaiseksi Javan kotisivulta osoitteesta http://java.sun.com/. Myös Java 2 SDK 1.2.1 tai uudempi tulee olla asennettuna. Palvelimen asennus ja konfiguraatio-ohjeet tulevat käsittelyyn koodiesimerkkien jälkeen, luvussa "Kääntäminen ja jakelu".

Mikä oikeastaan onkaan EJB?

Kuten jo mainitsin, EJB tulee sanoista Enterprise JavaBeans, joka usein suomennetaan sanalla "pavut". Itseasiassa papu antaa hieman harhaanjohtavan kuvan sillä kyseessä on eräänlainen komponenttipohjainen arkkitehtuuri eikä pelkästään yksittäinen papu, kuten nimestä voisi päätellä. EJB koostuu pääasiassa sovelluspalvelimesta, EJB-säiliöstä, muutamasta rajapinnasta, itse papuluokasta sekä asiakkaasta joka käyttää papua ja sen tarjoamia palveluita hyväkseen. Itseasiassa asiakas (client) näistä lienee kaikkein tutuin, mutta tässä opuksessa pyrin tekemään mahdollisimman ymmärrettäväksi myös muut edellä tulleet käsitteet. Tämän jälkeen pitää lähdekoodit kääntää normaaliin Java-tyyliin. Uutena featurena kehiin astuu kuitenkin pavun jakelu (deploy), joka kuitenkin luonnistuu J2EE:n mukana tulevalla Application Deployment Toolilla varsin kivuttomasti. Hieman pidemmälle luettuasi huomaatkin ettei kyseinen arkkitehtuuri nyt niin hirveän vaikea olekaan.

Sovelluspalvelin (Application Server)

Sovelluspalvelin on palvelin, jossa kaikki pavut toimivat ja sijaitsevat. Palvelin voi tosin samalla huolehtia monista muistakin tehtävistä kuten JavaServleteistä, JSP-sivuista, sekä WWW- ja FTP-palvelimen toiminnoista. Tässä oppaassa käytämme kuitenkin J2EE:n mukana tuleva sovelluspalvelinta. J2EE:n voit ladata Sunin sivuilta osoitteesta http://java.sun.com/. Yrityksissä tosin käytetään paljon BEA:n Weblogic Application Serveriä...

EJB-säiliö (EJB-Container)

Pavut sijaitsevat, toimivat ja elävät sovelluspalvelimella olevassa EJB-containerissa eli -säiliössä. Kyseessä on tärkeä osa EJB-sovellusta, sillä säiliö tarjoaa pavulle erittäin tärkeitä palveluita, hoitaa pavun ja asiakaan välisiä yhteyksiä, huolehtii sen rekisteröitymisestä nimipalveluun, tietokantayhteyksistä, tietoturvasta sekä hoitelee mm. pavun luonnin. Yleensä säiliön implementoinnista huolehtivatkin juuri sovelluspalvelimen kehittäjät. Säiliö huolehtii siis pavusta hieman samalla tavalla kuin JavaWebServer huolehtii servleteistä tai HTML-selain huolehtii appletista. Säiliö pitää myös huolen siitä ettei asiakasohjelma pääse suoraan käsiksi papuihin, vaan säiliö huolehtii kaikista yllämainituista asioista niin ettei ohjelmoijan itse tarvitse ohjelmoida näitä toiminnallisuuksia vaan voi keskittyä pääasialliseen toimintaan. Koska säiliö tavallaan oleilee papujen ja asiakkaan välissä ei asiakkaalla ole harmainta aavistusta säiliön papujen käyttäytymisestä. Mikäli papua ei käytetä, säiliö voi antaa sen välillä toisen asiakkaan käytettäväksi tai jopa häivyttää sen kokonaan serverin muistista, herättäen sen uudelleen vasta tarvittaessa. Koska asiakkaalla ei siis ole hajuakaan papujen toiminnasta, voi säiliö häätää pavun serverin muistista niin, että asiakkaan etä-refrenssi on yhä "päällä". Vasta kun asiakas tarvitsee pavulta jotain palvelua, säiliö "uudelleensynnyttää" pavun asiakkaan tarpeisiin vastaamaan. Papu siis käyttää säiliötä kaikkeen mitä se tarvitsee, toisin sanoen se toimii säiliön kautta. Papu toimii säiliön läpi kolmen mekanismin avulla: joko Callback-metodien, EJBContext-rajapinnan tai JNDI-rajapinnan kautta. Paneutumatta sen enempää kyseisiin mekanismeihin, kerron kuitenkin että EJB-spesifikaatioissa on määritelty tarkkaan kyseisten mekanismien toteutus, sillä tällä mahdollistetaan eri vendorien säiliöissä kehitettyjen papujen toimivuus myös toisen valmistajan säiliöissä. Tälläisia valmistajia ovat esim. BEA ja IBM.

Käytettävät rajapinnat (Remote Interface ja Home Interface)

Mikäli olet jaksanut lukea opasta tänne asti, odotuksesi palkitaan. Tässä kappaleessa tulee esiin ensimmäinen koodinpätkä, mikä tosin edellyttää jo pientä Java-kielen taitoa. Ohjelmoidaksesi palvelinpuolen EJB-komponentin tulee sinun ohjelmoida kaksi rajapintaa, joita asiakas käyttää pavun sisäisen ja ulkoisen rajapinnan määrittelyyn, sekä itse papuluokka joka sisältää toiminnallisuuden. Aluksi selvittelemme kuitenkin käytettäviä rajapintoja. Home - ja Remote Interfacet edustavat papua, mutta itseasiassa Container eristää asiakassovelluksen suoralta yhteydeltä papuun. Home Interface määrittelee pavun elämänkaaren kannalta olelliset metodit, esimerkiksi metodit create(), destroy() ja find(), kun taas Remote Interface määrittelee pavun business- tai sovelluslogiikan, kummaksi sitä nyt haluaakaan kutsua.

Remote Interface määrittelee pavun ulkoisen rajapinnan, tämä on siis juuri se rajapinta jolla myöhemmin ohjelmoitava asiakas osaa käyttää pavun toimintoja. Kyseinen rajapinta sisältää siis pavun sovelluslogiikan metodit ja periytyy javax.ejb.EJBObject -rajapinnasta. Otetaan esimerkiksi yksinkertainen Remote Interface, PuhelinMuistio.

import javax.ejb.EJBObject;
import java.rmi.RemoteException;
 
public interface PuhelinMuistio extends EJBObject
{
	public void asetaNimi(String nimi) throws RemoteException;
	public void asetaNumero(String numero) throws RemoteException;
 
	public String haeNimi() throws RemoteException;
	public String haeNumero() throws RemoteException;	
 
}

Nyt olemme luoneet rajapinnan, jossa määritellään itse papuluokan metodit. Rajapinta perii EJBObject rajapinnan, jossa määritellään joukko standardeja metodeja.


Muistathan aina tehdessäsi Remote Interfacea, että sen täytyy periytyä EJBObject-rajapinnasta.


Home Interface määrittelee rajapinnan, jonka avulla asiakas voi esim. luoda tai poistaa papuja. Home interface perityy javax.ejb.EJBHome rajapinnasta, sekä määritelee pavun elämänkaaren kannalta oleelliset metodit kuten create():n, mutta sisältää myös muita metodeita, joiden avulla voi etsiä tiettyjä papuja kuten esimerkissämme, PuhelinMuistiossa.

import java.util.Collection;
import java.rmi.RemoteException;
import javax.ejb.FinderException;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
 
public interface PuhelinMuistioHome extends EJBHome
{
	public PuhelinMuistio create(String id, String nimi, String numero)
		throws RemoteException, CreateException;
 
	public PuhelinMuistio findByPrimaryKey(String id)
		throws FinderException, RemoteException;
 
	public Collection findByName(String nimi)
		throws FinderException, RemoteException;
 
	public Collection findByNumber(String numero)
		throws FinderException, RemoteException;
 
}

findByName ja finByNumber -metodit palauttavat Collection-tyyppisen olion, joka voi sisältää eri määrän olioita (PuhelinMuistio-tyyppisiä papuja), joita käymme sitten läpi asiakasohjelmassa elementti elementiltä.

Näin olemme siis määritelleet kaksi rajapintaa, joiden avulla saatamme toteuttaa itse papuluokan sekä itse asiakasohjelman. Vaikka EJB-arkkitehtuuri saattaakin tähän asti kuulostaa varsin yksinkertaiselta, saatat tämän oppaan loppuun mennessä olla toista mieltä, vaikka tämä opas raapaisee vasta pintaa.

Bean Class

Bean Class, eli itse papu, implementoi joko EntityBeania tai SessionBeania. Sessiopavut eli SessionBeanit edustavat palvelimella asiakasta. EJB-säiliö pitää huolen ettei kaksi asiakasta voi edes viitata samaan sessiopapuun, joten jokainen sessiopapu on asiakkaalleen yksilöllinen. EJB-säiliö pitää huolen sessiopavun elinkaaresta, sen luonnista ja tappamisesta. Sessiopapuja on kahta tyyppiä, tilattomia (stateless) ja tilallisia (statefull). Tilattomalla sessiopavulla ei nimensä mukaisesti ole tilaa, joka voisi muuttua pavun elinkaaren aikana. Alla on vain esimerkin vuoksi eräänlainen tilaton sessiopapu (kyseinen ei kuitenkaan liity mitenkään PuhelinMuistio-papuumme), sillä kyseisellä sessiopavulla ei ole minkäänlaista tilaa, vaan jokainen kutsu voidaan periaatteesa aloittaa alusta.

import java.rmi.RemoteException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
 
public class TerveEJB implements SessionBean
{
	public String sanoTerve()
	{
	 return " Terveppa terve!!! ";
	}
 
	// nämä ovat metodeita jotka tulevat SessionBean rajapinnasta
	// ne on jätetty tyhjiksi koska EJB-säiliö huolehtii niistä.
 
	public TerveEJB() {}
	public void ejbCreate() {}
	public void ejbRemove() {}
	public void ejbActive() {}
	public void ejbPassivate() {}
	public void setSessionContext(SessionContext sc) {}
}

Toisin kuin tilattomia, tilallista sessiopapua voidaan käyttää esimerkiksi kaikenlaisissa ostoskorisovelluksissa, joissa jokaiselle asiakkaalle tarvitaan yksilöllinen tila.

EntityBeanit, eli yksilöpavut, edustavat aina jotain tallennettavaa tuotetta, kuten asiakas, jokin tuote tai vaikkapa tilaus. Yksilöpapujen tieto tallentuu melkein aika jonkinlaiseen relaatiotietokantaan, hyvä niin, sillä ne tarjoavat eräänlaisen oliomaisen rajapinnan tietoon, joka muuten pitäsi hakea jollain muilla keinoin, esimerkiksi JDBC-rajapinnan avulla. Tämä on juuri eräs paputekniikan hyvistä puolista. Ohjelmoija voi jättää moiset ongelmat säiliölle ja keskittyä olennaiseen eli itse businesslogiikan ohjelmointiin. Yksilöpapujakin on kahta tyyppiä, niitä joita säiliö ylläpitää (CMP, Container-Managed Persistence) sekä pavun itse ylläpitämiä (BMP, Bean-Managed Persistence). BMP-tyyppisessä pavussa papu itse huolehtii tiedon tallentamisesta, eli käytännössä ohjelmoija joutuu tässä tapauksessa itse ohjelmoimaan kohdat joissa tieto tallennetaan kantaan. CMP-tyylisissä pavuissa EJB-säiliö hoitaa pavun sisältämän tiedon tallennuksen kantaan. Käytämmekin PuhelinMuistio papu esimerkissämme juuri tämän tyyppistä papua demonstroimaan kuinka tietokantaan tallennuksen voi hoitaa kirjoittamatta lausettakaan SQL:lää. Alla olevassa listauksessa näkyy siis PuhelinMuistioEJB , eli itse papu luokka, huomaa että se sisältää niiden metodien toteutuksen mitkä määrittelimme PuhelinMuistio Intefacessa.

import java.util.*;
import javax.ejb.*;
 
public class PuhelinMuistioEJB implements EntityBean
{
 	public String id;
	public String nimi;
	public String numero;
 
	private EntityContext ctx;
 
	public void asetaNimi(String nimi)
	{
		this.nimi = nimi;
	}
 
	public String haeNimi() 
	{
		return nimi;	
	}
 
	public void asetaNumero(String numero)
	{
		this.numero = numero;
	}
 
	public String haeNumero()
	{
		return numero;
	}
 
	public String ejbCreate(String id, String nimi, String numero) throws CreateException
	{
	    	if(id==null)
		{ throw new CreateException("Tarvitaan id!"); }
		this.id = id;
		this.nimi = nimi;
		this.numero = numero;
 
		return null;	
	}
 
	public void setEntityContext(javax.ejb.EntityContext p1) throws javax.ejb.EJBException, java.rmi.RemoteException
	{
		this.ctx = ctx;
	}
 
	public void ejbActivate() throws javax.ejb.EJBException, java.rmi.RemoteException
	{
		id = (String)ctx.getPrimaryKey();
	}
 
	public void ejbPassivate() throws javax.ejb.EJBException, java.rmi.RemoteException
	{
		id = null;
		nimi = null;
		numero = null;
	}
	
	public void ejbRemove() throws javax.ejb.RemoveException, javax.ejb.EJBException, java.rmi.RemoteException {}
	public void ejbLoad() throws javax.ejb.EJBException, java.rmi.RemoteException {}
	public void ejbStore() throws javax.ejb.EJBException, java.rmi.RemoteException {}
	public void unsetEntityContext() throws javax.ejb.EJBException, java.rmi.RemoteException {}
	public void ejbPostCreate(String id, String nimi, String numero) {}
}

Alussa on määritelty kolme julkista jäsenmuuttujaa ( id, nimi, numero ) näistä käy ilmi pavun tila, eli henkilön nimi ja puhelinnumero. EntityContext tyyppi tarjoaa rajapinnan säiliön ylläpitämään
papuun ( tätä tarvitaan tietokanta tallennuksia ajatellen ). Papua luodessa riittää että sille välitetään parametreinä sen jäsenmuuttujien arvot, ohjelmoijan ei tarvitse välittää tiedon tallentamisesta mitään, sillä säiliö hoitaa itsekseen tiedon tallentamisen tietokantaan. CMP-tyyliset pavut ovat yleensä helpoimpia ohjelmoijan tehdä mutta vaikeimpia palveltavia mitä palvelimella voi olla. EJB-säiliöiden toimittajien ( BEA, IBM ) tarjoamat oliomaiset rajapinnat vaihtelevatkin melkoisesti juuri tämän takia. Toisten tarjotessa hyvin korkealaatuisia rajapintoja toisten jäädessä vähän raaemmalle tasolle. Mikäli haluaisimme tehdä BMP -tyylisen pavun olisi meidän ohjelmoitava koodi, jossa tietoa tallennetaan kantaan. Esimerkiksi meidän pitäisi ohjelmoida ejbLoad -metodi, jossa tietoa haetaan kannasta tietyin parametrein, kun taas ejbStore -metodin pitäisi päivittää tai tallentaa tiedot kantaan JDBC:n avulla. Mutta en käsittele BMP-tyylisiä papuja enää tässä tutoriaalissa sen tarkemmin.

Nyt meillä on siis kaksi rajapintaa Remote Interface ( interface PuhelinMuistio ), Home Interface ( interface PuhelinMuistioHome ) sekä itse papu-luokka ( PuhelinMuistioEJB ). Nyt jäljellä on enää pavun lähde koodien kääntämien ja jakelu ( deploy ) , sekä asiakasohjelman teko. Vaikka aikaa on mennyt toivottavasti vasta varsin vähän olet luultavasti jo oppinut melko paljon papu arkkitehtuurista. Seuraavaksi käännämme ja jakelemme pavun.

Kääntäminen ja jakelu

Tässä oppaassa teemme pavun jakelun käyttäen J2EE:n mukana tulevaa graafista työkalua. Selvitän kuitenkin muutamalla lauseella mistä tuossa jakelussa on oikein kysymys. Käsin tehtävässä jakelussa meidän olisi tehtävä XML-jakelu kuvaus ( XML deployment discriptor ), jossa kuvattaisiin pavun olemukseen liittyvää tietoa, juuri niitä tietoja, joita kävimme läpi muutamasa edellisessä kappaleessa ( Persistence tyypit sekä turvallisuus roolit ) . Tämän jälkeen meidän pitäsi pakata tämä XML-tiedosto yhdessä toteuttamiemme luokkien
ja rajapintojen kanssa JAR tiedostoon, josta EJB-säiliö voi lukea miten kyseisen pavun tulisi käyttäytyä ajonaikana. JAR -tiedoston tulee olla EJB-säiliö riippumaton, jotta papua voidaan käyttää missä sovelluspalvelimella tahansa. Tämän dokumentin teon ja kaiken muunkin jakeluun liittyvän hoitaa kuitenkin ja onneksi J2EE:n mukana tuleva Application Deployment Tool ( ADT ).

Ennenkuin papua voidaan jaella, täytyy sen lähdekoodit luonnollisesti kääntää. Tätä varten kannattaa tehdä ajettavia bat -tiedostoja ( komentoriviskriptejä ) , jotta ympäristömuuttujat saadaan kohdalleen. Alla muutama esimerkki, joissa J2sdkee1.2.1 on asennettu oletushakemistoonsa ( C:\j2sdkee1.2.1 ) . Kirjoita siis pari seuraavaa riviä tekstitiedostoon ja nimeä se tiedostonNimi.bat . Näin pystyt ajamaan komennot konsolissa käskyllä tiedostonNimi.

set CPATH = .;%PATH%;%J2EE_HOME%\lib\j2ee.jar javac -classpath %CPATH% *.java

ajaessasi tiedostonNimi.bat tiedoston kääntyvät kaiki java-lähdekoodisi oivasti

Ennen jakelua kannattaa kuitenkin käynnistää J2EE:n mukana tuleva tietokanta palvelin ( cloudscape ) sekä itse sovelluspalvelin. Ennen näiden käynnistämistä kannattaa kuitenkin muokata ja ajaa komentorivi skriptit, jotka löytyvät J2sdkee1.2.1\bin hakemistoista. Kyseisten tiedostojen nimet ovat setenv.bat ja userconfig.bat. Konfiguroimalla näiden tiedostojen PATH, J2EE_HOME ja JAVA_HOME arvot sinulle sopiviksi, voit ajaa ne jälleen konsolissa. ennen varsinaisten palvelimien käynnistystä.

Tietokantapalvelin Cloudscape käynnistetään seuraavasti C:\j2sdkee1.2.1\bin>cloudscape -start

sekä itse sovelluspalevelin seuraavasti C:\j2sdkee1.2.1\bin>j2ee -verbose


Voit testata palvelimen toiminnan , menemällä selaimella http://localhost:8000, jolloin, jos palvelin on oikein käynnistetty näkyy seuraavanlainen kuva: Kuva:j2eeServer.jpg


molemmat näistä tulevat siis J2EE:n mukana. Muista kuitenkin ajaa komentoriviskriptit, joka kerta kun ajat/käännät/käynnistät jotain sovellustasi, sillä tämä varmistaa että kaikki ympäristömuuttujat on oikein asetettu.

Seuraavaksi aloitamme pavun jakelun, Application Deployment Toolilla. Se käynnistyy komentoriviltä käskyllä deploytool. C:\j2sdkee1.2.1\bin>deploytool ( Muistitahan ajaa komentoriviskriptit )

ja näyttää tältä käynnistyessään....


Valitse nyt ohjelman valikosta File->new Application, jolla luodaan uusi jaeltava sovellus. Anna sovellukselle nimeksi PuhelinMuistioApp.ear , painamalla ok pitäisi perusasetuksien näyttää tältä ,

Kuva:EJBdeptoolApplication.jpg

Seuraavaksi lisätään sovellukseen papuluokat, File -> New Enterprse Bean valikosta. Paina Add-nappia ja valiset juuri luokka missä pavut sijaitsevat, tai jos olet määritellyt packaget niin sitten se juurihakemisto mitä haluat käyttää.

Kuva:EJBinsertingBeans.jpg

(Huomaa että minulla pavut sijaitsevat jsse-packagessa, joita ei näy lähdekoodeissa , joten sinun pitää valita juurihakemistoksesi se hakemisto missä pavut sijaitsevat )


Sitten määrittelemme mitkä toimivat rajapintoina ja mikä itsepapuluokkana siellä EJB JARrissa. Valitse Enterprise Bean Classiksi itse papu luokka eli PuhelinMuistioEJB, sekä Home- ja Remote rajapinnoiksi myös oikeat tiedostot. Tämän jälkeen laita Bean Tyypiksi Entity, sillä semmoisenhan me juuri teimme. Laita myös pavun Display Nameksi PuhelinMuistioBean, tämän jälkeen voi painaa next-painiketta. (Huomaa että kuvassa luokat sijaitsevat jsse-packagessa, joita ei näi esimerkki lähdekoodeissa )

Kuva:EJBentityBean.jpg

Seuraavaksi kerromme jakelutyökalulle että kyseessä on säiliön hallitsema papu, joten klikkaamme Container Managed Persistence nappia ja valitsemme id, nimi, numero kohdat valituiksi, sekä asetamme pääavaimeksi id:n , sekä tälle tyypiksi Stringin.

Kuva:EJBCMPpapu.jpg

Nyt määritellään jakelu-asetukset. Valitse ADT:n vasemmalla olevasta valikosta PuhelinMuistioBean ja tämän asetuksista Entity-välilehti.

Kuva:EJBBeanEntity.jpg

Paina tällä lehdellä olevaa Deployment Setting nappia, joilloin voit asettaa jakelu asetukset. Anna Databasen JNDI nimeksi jdbc/Cloudscape, tästä käy ilmi tietokannan nimi. Tarkista että DataBase Tablen, Create table on Deploy kenttä on valittuna.

Paina nyt Generate SQL -nappia , jolloin ADT luo sql lauseet alla oleville metodeille. Tämän jälkeen se pyytää sinua lisäämään WHERE ehdot kahdelle metodille ( findByName ja findByNumber ). Tee se lisäämällä kyseinen teksti SQL Statement kenttään kyseisten EJB metodien kohdalle WHERE "nimi" = ?1 / WHERE "numero" = ?1 , ?1 tarkoittaa kyseisen metodin ensimmäistä parametriä, eli toisessa tapauksessa se viittaa numeroon ja toisessa metodissa nimeen. Paina vain nyt ok nappia niin olet taas päävalikossa.

Kuva:EJBsqlBeans.jpg


Tämän jälkeen voit painaa next nappia niin kauan kuin saavut seuraavan laiseen ruutuun

Kuva:EJBtransactionBean.jpg

Aseta tästä metodien, asetaNimi, asetaNumero, haeNimi ja haeNumero, tyypiksi required, ( Ne ovat oletuksena NotSupported ) jolloin säiliö tietää ja huolehtii näiden metodien suorittamien transaktioiden hallinnasta. Tämän jälkeen voit painaa finnish painiketta.

Sieltä valitset nyt PuhelinMuistioApp solmusta JNDI-names välilehden ja asetat tälle JNDI-nimeksi myPuhelinMuistio, ( tätä nimeä käytetään clientissa )

Kuva:EJBjndiBean.jpg


Nyt valitset alla olevan kuvan mukaisesti ADT:n tools valikosta Deploy Application kohdan, ruksaat kohdan Return Client Jar, ja annat tälle nimen ( tämä .jar tiedosto asetetaan clienttia ajettaessa komentoriviskriptillä ympäristömuttujaan, joten kannattaa laittaa se hakemistoon josta sen helposti löytää ).

Kuva:EJBreturnJAR.jpg

Paina nyt vain sitten Finnish painiketta, jolloin voi vain enää toivoa että kaikki menee hyvin. Joillakin kokoonpanoilla ja eri Web- / Application servereillä on ollut vaikeuksia ymmärtää tätä kaikkea, saati sitten toimia yhdessä ja sulassa sovussa, mutta kaiken kaikkiaan, mikäli jakelu onnistuu pitäisi seuraava kuva olla ruudussa hetken päästä.

Kuva:EJBdeployed.jpg

Asiakasohjelman toteutus.

Nyt olemme siis luoneet pavun, ja jaelleet sen, nyt on enää client-jäljellä. Mikäli olet toiminut oikein sinulla on vielä tässäkin vaiheessa J2ee Application Server ja Cloudscape tietokanta palvelin päällä, joten tulee helpommaksi testata clienttia =).

Seuraavassa koodiassa luodaan yhteys papuun ja käytetään sen palveluja luomiemme rajapintojen kautta. Koodi on kommentoitu osaksi. mutta käyn sitä vielä läpi tuolla alempana. Mikäli haluat vain kopioida sorsat tee se nyt , ja käännä ja aja clientti, mutta muista Ajaessasi asiakasohjelmaa muista ajaa ensin komentoriviskriptit asettaaksesi ympäristömuuttujat oikein. Itse tein seuraavanlaisen PuhelinMuistioClient.batin...

set CPATH=.;%PATH%;%J2EE_HOME%\lib\j2ee.jar;%J2EE_HOME%\bin\PuhelinMuistioClient.jar java -classpath %CPATH% PuhelinMuistioClient

huomio kuitenkin että asetat hakemistot kohdilleen, niinkuin ne ovat sinun ympäristössäsi.

import java.util.*;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;
 
public class PuhelinMuistioClient
{
	public static void main(String[] args)
	{	
	  try{
		Context init = new InitialContext();
 
		// myPuhelinMuistio on PuhelinMuistio pavun JNDI nimi
		Object oref = init.lookup("myPuhelinMuistio");
		PuhelinMuistioHome home = (PuhelinMuistioHome)
                    PortableRemoteObject.narrow(oref, PuhelinMuistioHome.class);
 
 
		System.out.println("Luodaan henkiloita...");
		PuhelinMuistio pm = home.create(" 1111 ", " Jabba Da Hut ",  " 555-5555 ");
		pm = home.create(" 1112 ", " Luke Skywalker ", " 111-1111 ");
		pm = home.create(" 1113 ", " Darth Vader ", " 111-1111 ");
		pm = home.create(" 1114 ", " Jamppa Tuominen ", " 444-4444 ");
 
		// uhups Jampalla onkin vaihtunut numero
 
		pm.asetaNumero(" 444-4443 ");
 
		// etsitään heppua id:llä
                System.out.println("Etsitaan heppua id:lla...");
 
		PuhelinMuistio etsi = home.findByPrimaryKey(" 1112 ");
                System.out.println("Etsitaan henkiloa PrimaryKey 1112:lla");
		System.out.println("Loytyi -> nimi: "+etsi.haeNimi()+" numero: "+etsi.haeNumero());
                    
 
		  // etsitään heppua nimellä
                System.out.println("Etsitaan heppua nimella...Jamppa Tuominen");
       
		Collection col = home.findByName("Jamppa Tuominen");
		Iterator it = col.iterator();
		while( it.hasNext())
 
		{
			PuhelinMuistio puh = (PuhelinMuistio) it.next();
			String id = (String)puh.getPrimaryKey();
			String nimi = puh.haeNimi();
			String numero = puh.haeNumero();
			System.out.println("Loytyi -> id: "+ id + " nimi: "+nimi+ " num: "+numero);
			
		}
                
 
	    } 
            catch ( Exception e )
		{
			System.err.println("Exception caught...");
			e.printStackTrace();	
		}
   
	}	
}

Ensin tietysti importataan tarvittavat luokat, kuten normaalistikkin. Sen jälkeen käytetään InitialContext -luokkaa muodostamaan yhteys nimipalvelimeen ja sen lookup -metodia, joka palauttaa tietynnimisen olion Object-yläluokassa. Sen jälkeen narrow -metodilla tarkistetaan voidaanko palautettu objekti tyyppikonvertoida , Noiden luokkien tarkemmasta toiminnasta saa tietoa J2EE dokumentoinneista. Sitten vain luodaan henkilöt, create() -metodilla, sekä käytetellään hieman noita business-logiikkaa metodeita. Kuten muistamme findByName() -metodi palauttaa Collectionin joten, se joudutaan käymään läpi silmukassa, käyttäen apuna Iterator-luokkaa. ( Katso Collectioni luokan toiminta tarkemmin Java Apista, jos kiinnostaa ).

Nyt voitkin ajaa PuhelinMuistioClient.batin , jotta ympäristömuuttujat saadaan asetettua, ja clien toimintaan Mikäli kaikki on mennyt hyvin niin clientin pitäisi toimi ja tulostaa seuraavanlainen teksti...


Kuva:EJBresult.jpg

( itseasissa ilman noita muutamaa virheellistä ajokertaa =) , muuta en jaksanut enää ottaa capturea pelkästä onnistuneesta ajokerrasta ). Tuo DuplicateKeyException johtuu siitä että, client ei poista henkilöitä, joten ajaessasi clienttia useamman kerran peräjälkeen, syntyy noita tupla Primary avaimia, jolloin Exception heitetään. Tämän voit korjata kommentoimalla luomisrivit clientista tai laittamalla joka kerralla uuden PrimaryKeyn henkilöille. Mutta tässä oli kaikki tällä erää, joten iloisia EJB-toteutushetkiä kaikille. Lisäharjoituksena lisätä clienttiin vaikkapa metodin , jossa haetaan tietoja numeron perusteella ( findByNumber -metodilla ). Tämäkin siis paulauttaa Collectionin, joka voidaan käydä samanlailla läpi kuten tehtiin juuri meidänkin clientissa. Paitsi että tämän pitäisi palauttaa kaksi objektia, sillä jos olitte tarkkana huomasitte että kahdella hemmolla onpi sama numero....hih.

Henkilökohtaiset työkalut