1 Overordnet om matrikkeltjeneren
2 Innsyn og oppdatering via oppdateringsapi
3 Prinsipper for domenemodellen
4 Håndtering av mer komplekse matrikkeldata
Matrikkelsystemet er basert på Java/J2EE teknologi og en sentral applikasjonstjener tilbyr et tjeneste-orientert grensesnitt for all aksess til systemet. Denne arkitekturen baserer seg på en tre-lags arkitektur der tjeneren håndterer forretningslogikk og innkapsler all kommunikasjon med databasene.
Matrikkeltjeneren tilbyr et tjeneste-orientert grensesnitt som alle klienter kan benytte for kommunikasjon med systemet. I begrepet tjeneste-orientert ligger følgende:
Tjenestene er hovedsaklig gruppert etter en inndeling i funksjonelle områder. Eksempel på tjenester er:
Alle tjenestene er implementert som Enterprise JavaBeans av typen Stateless Session Beans.
Domenemodellen er en objekt-orientert modell av datene i matrikkelen. Domenemodellen er én felles modell som benyttes av alle tjenester. Det vil si at alle tjenester hovedsaklig benytter domenemodellen for parametre og returverdier.
Eksempler på domeneobjekter i matrikkelsystemet er:
Alle feil som oppstår under prosessering på matrikkeltjeneren vil meldes til klienten som unntak (exception). Det finnes to hovedgrupper unntak:
For å få adgang til matrikkeltjeneren må klienten autentisere seg med en gyldig bruker i form av brukernavn og passord. Dette skjer i forbindelse med at man
Matrikkeltjeneren vil kjøre på en plattform med lastbalansering og høy tilgjengelighet. Dette innebærer blant annet at det vil være en klynge av matrikkeltjenere som utad fungerer som en logisk tjener.
Dette kapittelet beskriver hovedprinsippene for oppkobling og bruk av tjenester i oppdateringsapi’et. Dette api’et gir tilgang til uthenting og oppdatering av alle data i Matrikkelen. Således kan det også benyttes som et innsynsapi for klienter som vil hente all informasjon om objekter i matrikkelen. Tilgang til oppdateringsfunksjoner vs. uthentingsfunksjoner vil styres av rettighetene til brukeren som er pålogget.
Distribusjon av klientkode inneholder:
Klientsidekoden inneholder klientsidefunksjonalitet for å håndtere slike ting som login og caching av id’er.
Klientsidekoden vår er ment som et tilbud til leverandører som skal implementere klienter til matrikkelen. Den bakenforliggende teknologien i oppdateringsAPIet er EJB'er (RMI over IIOP). Dette er kjent teknologi som leverandørene godt kan aksessere direkte hvis de heller skulle ønske det enn å benytte klientsidebiblioteket vårt.
Klientsidekoden er implementert i Java. Leverandører kan velge å enten benytte Java selv, eller bruke en såkalt .Net-Java bro for å kunne implementere i .Net. Kartverket har benyttet JNBridgePro 3.0 i sin integrasjonstesting, men andre bro-produkter skal også kunne fungere, f.eks. JIntegra Espresso eller JuggerNET.
OppdateringsAPI’et består av flere tjenester. Disse kan deles opp i hovedfunksjonalitet og støttetjenester, støttetjenester i kursiv brukes kun indirekte.
Hovedfunksjonalitet:
Støttetjenester/Infrastrukturtjenester:
Noen ting å merke seg angående tjenestene er:
Alle hovedtjenestene benyttes både ved lesing og skriving av data, men man må forholde seg forskjellig til StoreService.
Bruken av Tjenestene ved lesing av data følger denne strukturen:
Her er to enkle eksempler som leser data i hhv. Java og C#:
public void testFindVegadresse_Success() {
LoginService.Accessor.get().login("test1", "matrikkel");
Kommune nittedal = KommuneService.Accessor.get().findKommune("0233");
Vegadresse vadresse = AdresseService.Accessor.get().findVegadresse(nittedal.getId(), 4200, 13, "B");
assertNotNull(gadresse);
assertEquals("Feil husnr", 13, gadresse.getHusnr());
assertEquals("Feil bokstav", "B", gadresse.getBokstav());
}
public void testFindVeg() {
LoginService.Accessor.get().login("test1", "matrikkel");
AdresseService adresseService = AdresseService.Accessor.get();
Kommune nittedal = KommuneService.Accessor.get().findKommune("0233");
Veg veg = adresseService.findVeg(nittedal.getId(), 4200);
Assert.Equals("Gamle Trondheimsvei".ToUpper(), veg.getAdressenavn().ToUpper(), "Feil adressenavn");
Assert.Equals(4200, veg.getAdressekode(), "Feil adressekode");
}
Oppdatering (update, delete) fungerer noe annerledes enn lesing.
Bruken av Tjenestene ved oppdatering følger denne strukturen:
Bruken av tjenester for oppdatering av matrikkeldata er tilsvarende som for uthenting av data. Men det er noen viktige aspekter rundt oppdatering:
StoreService.get(obj, Lock.WRITE)
, så forblir
det låst til enten objektet benyttes i en av oppdateringstjenesten (f.eks. updateVegadresse) eller
til dere kjører StoreService.unlock(obj);
Her er to eksempler som oppdaterer data i hhv. Java og C#:
public void testUpdateVegadresse() {
LoginService.Accessor.get().login("test1", "matrikkel");
Kommune nittedal = KommuneService.Accessor.get().findKommune("0233");
Vegadresse vegadresse1 = AdresseService.Accessor.get().findVegadresse(nittedal.getId(), 4200, 13, "B");
vegadresse1 = (Vegadresse) StoreService.Accessor.get().get(vegadresse1.getId(), Lock.WRITE);
vegadresse1.setHusNr(15);
vegadresse1.setBokstav(””);
AdresseService.Accessor.get().updateVegadresse(vegadresse1);
Vegadresse vegadresse2 = (Vegadresse) StoreService.Accessor.get().get(vegadresse1.getId(), Lock.NONE);
assertNotNull(vegadresse2);
assertEquals("Feil husnr", 15, vegadresse2.getHusnr());
assertEquals("Feil bokstav", "", vegadresse2.getBokstav());
}
public void testUpdateVegadresse(){
AdresseService adresseService = AdresseService.Accessor.get();
StoreService storeService = StoreService.Accessor.get();
Kommune nittedal = KommuneService.Accessor.get().findKommune("0233");
Vegadresse vegadresse1 = adresseService.findVegadresse(nittedal.getId(), 4200, 13, "B");
vegadresse1 = (Vegadresse) storeService.get(vegadresse1.getId(), Lock.WRITE);
vegadresse1.setHusnr(15);
vegadresse1.setBokstav("");
adresseService.updateVeg(veg);
vegadresse2 = (Vegadresse) storeService.get(vegadresse1.getId(), Lock.NONE);
Assert.Equals(15, vegadresse2.getHusNr(), "Feil husnr");
Assert.Equals("", vegadresse2.getBokstav(), "Feil bokstav");
}
Tjenester som lagrer nye matrikkelobjekter forutsetter at domenebobler på forhånd er tildelt en unik id. Det er kun matrikkeltjeneren som kan tildele nye unike id’er, dette gjøres gjennom en egen service (IdService).
Objekter som ligger inne i domenebobler og som dermed ikke er en subklasse av MatrikkelBubbleObject skal ikke tildeles id’er før lagring, dette gjøres automatisk av tjeneren.
Se kap. 3.2 for en nærmere forklaring på forskjellen mellom domenebobler og komponenter.
Bruken av Tjenestene må derfor følge denne strukturen:
Her er et eksempel som legger inn et nytt objekt:
public void testInsertVegadresse () {
LoginService loginService = LoginService.Accessor.get();
KommuneService kommuneService = KommuneService.Accessor.get();
AdresseService adresseService = AdresseService.Accessor.get();
loginService.login("test1", "matrikkel");
Kommune nittedal = kommuneService.findKommune("0233");
List veger = adresseService.findVegerMedNavn(nittedal.getId(),0, "sollia");
VegId vegId = ((Veg)veger.get(0)).getId();
List adresser = adresseService.findAdresserForVeg(vegId);
int gammeltAntallVeger = adresser.size();
Vegadresse nyVegadresse = new Vegadresse();
idService.assignId(nyVegadresse);
nyVegadresse.setHusnr(14);
nyVegadresse.setVegId(vegId);
Representasjonspunkt rep = new Representasjonspunkt(CoordinateSystem.EUREF_SONE33, 6656515.30, 273344.60, 0.00);
nyVegadresse.setRepresentasjonspunkt(rep);
adresseService.insertVegadresse(nyVegadresse);
adresser = adresseService.findAdresserForVeg(vegId);
assertEquals("Vegen skulle fått lagt inn en vegadresse til", (gammeltAntallVeger + 1), adresser.size());
}
Når man skal gjøre oppdateringer i systemet så må man som vist over låse objektene som skal oppdateres først. Systemet fungerer slik at for hver transaksjonell update tjeneste som fullfører med suksess så låses alle låsene til den brukeren opp. Dette innebærer at hvis man ønsker å gjøre flere oppdateringer rett etter hverandre så må man låse de nødvendige objektene på nytt mellom hver oppdatering.
Det er også slik at systemet kun låser opp objekter ved en vellykket opdpatering. Hvis en feil oppstår under et update-kall så vil låsene som brukeren hadde før update-kallet begynte fremdeles være låst etter at kallet har feilet. Hvis brukeren avbryter operasjonen eller et kall feiler så er det opp til klientprogrammet å håndtere dette korrekt. Det vil f.eks. være opp til programmet å sørge for å kalle StoreService.unlock() for å låse opp de låste objektene.
Domenemodellen inneholder representasjon av alle dataene i matrikkelsystemet. Matrikkelsystemet baserer seg på en objekt-orientert modell der data og forretninglogikk er innkapslet. Domenemodellen kan beskrives fra to forskjellige synsvinkler:
Dersom analysemodellen ble direkte overført til en designmodell ville dette gitt en domenemodell der mer eller mindre hele domenemodellen hang sammen med harde referanser (pekere). Dette gir en del utfordringer i forhold til kommunikasjon mellom klient og tjener:
Den ene tjenesten skal f.eks. bare skal oppdatere en adresse, mens en annen skal oppdatere både adresser og dens tilhørende bruksenheter/bygninger. En tjeneste skal oppdatere adresser på en matrikkelenhet, mens en annen skal oppdatere matrikkelenheten og dens teiger.
Et enkelt objekt kan være referert til fra mange andre objekter. Eksempelvis kan en vegadresse være referert til fra både en veg, en bruksenhet og en matrikkelenhet. På klienten ønsker vi ikke multiple instanser av delte objekter. Spesielt vil dette lett kunne forekomme ved uthenting av data gjennom en serie av kall til matrikkeltjeneren.
Skal kun matrikkelenhet låses dersom teiggrensene skal justeres? Hva med delte grenser mellom eiendommer? Må også adresser tilknyttet matrikkelenheten låses?
Matrikkelsystemet løser disse utfordringene med en designmodell som deler analysemodellen opp i veldefinerte domenebobler. En domeneboble består av en samling assosierte domeneobjekter som behandles som en enhet med hensyn til uthenting og oppdatering. Alle objektene inni en domeneboble refererer til hverandre ved hjelp av harde referanser (pekere et programmeringsspråk).
Alle objektene i domeneboblen eies av et rot-objekt. Dette har en id som er en global id for hele boblen. Objekter utenfor domeneboblen kan bare refereres gjennom rot-objektet til dens domeneboble. Referanser til objekter utenfor domeneboblen implementeres da som myke referanser (referansen er da id’en for rot-objektet i den andre domeneboblen).
En del regler gjelder for bruk av domenebobler:
Domenebobler tilsvarer Eric Evans begrep Aggregate [Evans].
I designmodellen vil for eksempel Matrikkelenhet være rot-objekt for en domeneboble. Referanser til andre domeneobjekter gjøres ved at objekter i boblen har referanser til id'en til rot-entiteten i de respektive bobler. Dette ser vi i eksempelet under ved at relasjonen til kommune gjøres ved referanse til KommuneId.
Domeneboble for Matrikkelenhet:
Domeneboble for Adresse:
Domeneboble for Bygg:
Hvilke domenebobler som finnes kan sees i analysemodellen.
For enkelte deler av domenemodellen vil det være naturlig at tjenestene arbeider med mange domenebobler. For eksempel gjelder dette matrikkelenheter og geometri for disse. I slike tilfeller kan det være nødvendig med oversendelse av en større mengde domenebobler til/fra en tjeneste.
En tjeneste for å lagre en matrikkelenhet med oppdatert geometri trenger både bobler av typen Matrikkelenhet, Teig, Teiggrense og CurveSegment. For å håndtere slike tilfeller brukes en UnitOfWorkTransfer. Dette er et objekt brukt for overføring av data over nettverket. Egne UnitOfWorkTransfer defineres for de enkelte tjenester. Egenskapene til en UnitOfWorkTransfer er:
Tjenester for uthenting vil også kunne defines til å returnere flere domenebobler. For eksempel av ytelseshensyn (færre runder over nettverket) eller for å sikre konsistent låsing. Siden en tjeneste bare kan ha en returverdi må dette gjøres gjennom et eget objekt som holder på alle boblene. Til dette bruk har man en BubbleTransfer, egenskapene til denne er:
For dokumentasjon av domeneboblene i oppdateringsmodellen så har vi lagt inn en referanse til kjernemodell dokumentasjonen. Dvs. at for alle klasser som er med i en domeneboble vil det bare være en referanse videre til dokumentasjonen av denne klassen i kjernen.
For tjenestene så vil alle metodene være dokumentert. Hvilke klasser som kan/skal være med i et transfer-objekt (UnitOfWorkTransfer eller BubbleTransfer) er beskrevet under den enkelte metode med referanser til klassene i kjernemodellen.
Online JavaDoc for APIet.
Det er ikke egne UML diagrammer for oppdateringsapi, da datamodellen er basert på den underliggende
domenemodellen for matrikkelen.
UML diagrammer for domenemodellen finnes her,
og detaljert tekstlig dokumentasjon finnes her.
Her kan dere laste ned nødvendig programvare:
Zip fil som inneholder .jar-filer som trengs for å bygge .dll vha JNBridgePro og Visual Studio 2005 prosjekt med eksempel-kode
Zip-filen inneholder alle nødvendige filer både for en ren Java oppkobling og for en .NET til Java
oppkobling via JNBridgePro (v. 3.2). For .NET må det manuelt genereres en Proxy.dll for mapping av .NET til
Java via JNBridgePro, og man må ha en korrekt oppsatt App.config
for prosjektet for at
classpath som JNBridgePro bruker skal bli riktig.
Endelig må man ha installert JNBridgePro med gyldig lisens, se JNBridgePro.
Zip-filen inneholder 2 nødvendige filer fra JNBridgePro v. 3.2: JNBSharedMem.dll, JNBShared.dll. Dersom en annen versjon av
JNBridge benyttes bør disse erstattes med tilsvarende versjoner i henhold til JNBridgePro dokumentasjonen.
7 Kort innføring i JNBProxy fra JNBridgePro
JNBProxy brukes for å generere en .Net dll fra jar-filene som følger med Zip-filen over. Det er to fremgangsmåter for å generere en dll fil med proxyer:
v3-proxy-<versjon>.jnb
i samme katalog som jar-filene og dll'ene.
oppdateringapi_v3-client-<versjon>.jar
og matrikkelspif-extapi-<versjon>.jar
, OK.
v3-proxy-<versjon>.dll
og legges i prosjektroten sammen med de andre dll'ene.
Når du bruker den genererte dll-en i et .Net-prosjekt må du sette opp konfigurasjon for JNBProxy i en applikasjonskonfigurasjonsfil (app.config), som beskrevet i JNBridgePro users guide i kapittelet Configuring the .NET side:
<dotNetToJavaConfig scheme=”sharedmem” jvm=”full path to jvm.dll in JDK or JRE” jnbcore=”full path to jnbcore.jar” bcel=”full path to bcel-5.1-jnbridge.jar” classpath=”semicolon-separated Java classpath”/>classpath må her inneholde alle jar-filene i klient-zip-filen, samt rotkatalogen der du har pakket ut innholdet. Rotkatalogen er nødvendig fordi denne inneholder en weblogic lisensfil som er nødvendig for ssl kommunikasjon. Man kan enten angi absolutte stinavn eller relative stinavn. Relative stinavn må være i forhold til hvor
applikasjonsnavn.exe
og applikasjonsnavn.exe.config
ligger og ikke i forhold til App.config
.
Se innholdet av medfølgende App.config
for nødvendige jar filer.
For å kunne koble opp mot en backupserver så må filen
matrikkel_klient.properties endres til å inneholde korrekt
ip-nummer og port. Dette kan gjøres ved å legge inn en egen matrikkel_klient.properties fil i
classpathen til JNBridge. Man kan enten oppdatere filen som ligger i oppdateringapi_v3-client-<versjon>.jar
eller legge en ny fil i f.eks rotkatalogen som er inneholdt i classpathen. Det er da viktig at denne katalogen
står først i classpathen. Det er også mulig å sette oppkoblingsserver og port programmatisk. Se
src.test\SimpleTestClient.cs
for eksempel på hvordan dette gjøres.
Note: While shared memory is faster than binary/TCP, it has not been chosen as the default setting due to certain limitations in the Java Native Interface (JNI), which require that JNBProxy be exited and restarted whenever JNBProxy’s classpath is changed. If this is not an issue, using shared memory with JNBProxy is recommended.