Kennismaking met Ozone

Leo Rutten


      
     

Deze tekst mag verspreid worden voor niet-commerciële doeleinden.

Wijzigingen
Herziening 0.1.18/9/2003
Vertaling verder gezet.
Herziening 0.1.05/1/2002
Eerste versie gemaakt.

Samenvatting

Deze tekst geeft een kennismaking op de Ozone database. Ozone is een zuiver object-georiënteerde database die volledig in Java is geschreven. De objecten in de database worden door een server beheerd. De toegang tot de objecten verloopt transaparant via RMI. Ozone ondersteunt transacties.


Inhoudsopgave

1. Inleiding
1.1. Eigenschappen van Ozone
1.2. Waarom een andere dan een relationele database?
1.3. Ontwerpdoel / Architectuur
1.3.1. Doel
1.3.2. Architectuur
1.4. Ozone documentatie
1.4.1. Verschillende implementaties van OzoneInterface
1.4.2. Proxy objecten
1.4.3. Logica aan serverzijde
1.4.4. Transacties
1.5. Kennismaking met Ozone
1.5.1. Overzicht
1.5.2. Basisbegrippen
1.5.3. De Car interface
1.5.4. The CarImpl class
1.5.5. OPP en Proxy objecten
1.5.6. Een eenvoudige client applicatie
1.5.7. De Garage interface en de GarageImpl klasse
1.5.8. De GarageApp applicatie
1.5.9. Samenvatting
1.6. Server Configuratie
1.6.1. Heap grootte van de VM
1.6.2. B-Tree buffer/cache grootte
1.7. Vraag en antwoord
2. Grote Ozone voorbeelden
2.1. Erfenis
2.1.1. Auto
2.1.2. AutoImpl
2.1.3. Bus en BusImpl
2.1.4. Client
2.1.5. Client2
2.2. Lijst
2.2.1. Appl
2.2.2. App2
2.2.3. App3
2.2.4. App4
2.2.5. Database objecten voor Lijst voorbeeld
2.3. Lijst3
2.3.1. Car
2.3.2. CarImpl
2.3.3. Garage
2.3.4. GarageImpl
2.3.5. Lijst
2.3.6. LijstApp
2.3.7. LijstNotify
2.3.8. LijstServer
2.3.9. LijstWatch
2.4. Weersmeting
2.4.1. WeersGegevens
2.4.2. WeersGegevensImpl
2.4.3. Plaats
2.4.4. PlaatsImpl
2.4.5. Soort
2.4.6. SoortImpl
2.4.7. Meting
2.4.8. MetingImpl
2.4.9. Weer2
2.4.10. Weer3
2.4.11. Weer4
2.4.12. Weer5
2.4.13. WeerBis
2.4.14. weer.xml
2.4.15. weer-html.xsl

Hoofdstuk 1. Inleiding

1.1. Eigenschappen van Ozone

Java is een object-georientëerde database die volledig in Java is ontwikkeld. Ozone wordt verspreid met een open source licentie. Dit betekent dat er geen kosten verbonden zijn aan het gebruik van Ozone.

We overlopen even de eigenschappen van Ozone.

  • Ozone is een object-georiënteerde database. Database schema's en queries in een andere taal zijn niet nodig.

  • Persistente objecten gedragen zich als normale objecten. Ozone is daarom (bijna) volledig transparant. Dit betekent dat de objecten van een bestaande klassehierarchie met slechts een beperkt aantal aanpassingen in een Ozone database kan opgeslagen worden.

  • Ozone maakt gebruik van RMI voor de toegang tot persistente objecten. Bijgevolg blijven deze objecten op de server. Wanneer een client toegang verlangt tot een object, wordt dit object eerst geactiveerd. Dit betekent dat het object in de server gecreëerd wordt en dat zijn toestand gelezen wordt uit de databaseopslag.

    Deze eigenschap heeft voor- en nadelen. Het nadeel is natuurlijk de overlast die RMI met zich meebrengt. Daarom is Ozone ideaal voor toepassingen waar meerdere clienten de database wijzigen.

  • Ozone ondersteunt transacties. Indien er niet expliciet transacties worden gemaakt, worden de methodeoproepen automatisch met transacties afgegrensd.

    Ozone is niet afhankelijk van één of andere database of mapping technologie voor de eigenlijke opslag van de objecten op de schijf. Ozone heeft zijn eigen systeem van cache en schijfopslag voor Java objecten.

  • Geen back-end database nodig.

    Ozone is niet afhankelijk van één of andere database of mapping technologie voor de eigenlijke opslag van de objecten op de schijf. Ozone heeft zijn eigen systeem van cache en schijfopslag voor Java objecten.

  • Ozone bevat een DOM implemantatie die voldoet aan de W3C specificaties. Hiermee is het mogelijk om XML data rechtstreeks in de database op te slaan.

  • Naast de Ozone API is er ook een ODMG 3.0 interface. ODMG is een poging om de interface tot object-georiënteerde databases te standaardizeren.

1.2. Waarom een andere dan een relationele database?

Een database zoals Ozone sluit beter aan bij het object-georiënteerd ontwerpen in Java. Er wordt vermeden dat naast het Java objectenmodel in UML er nog een datamodel voor een relationele database moet ontworpen worden. Je kan in plaats van Ozone of een andere object-georiënteerde database ook een relationeel-object mapper gebruiken zoals JDO.

1.3. Ontwerpdoel / Architectuur

1.3.1. Doel

Het hoofddoel van of Ozone is het bijvoegen van persistentie en transacties in het Java objectmodel op een transparante wijze. Ozone toepassingen moeten eruit zien en werken zoals gewone Java toepassingen. Nieuwe concepten moet je alleen maar introduceren als dit absoluut nodig is.

Ozone introduceert geen nieuw soort systeem, object- of berekeningsmodel, of query taal zoals relationele gegevensbanken en ODMG/OQL. Ozone laat je werken met persistente Java objecten in een transactionele omgeving. Ozone objecten zijn Java objecten. Geen andere dan Java types, geen andere query/update taal dan Java, geen impedantie problemen!

Ozone laat je toe te werken met objecten die inherent persistent zijn. Zulke objecten overleven de levensduur van een VM en tegelijkertijd kunnen ze zoals gewone Java objects zijn. ozone geeft de programmeur de illusie om toegang te krijgen tot een set objecten, die virtueel een oneindig aantal objecten kunnen bevatten onafhankelijk van de VM van de client toepassing. Dit is een nieuwe, pure object geöriëorienteerd zicht op een gegevensbank.

Een ander doel is het onnodig kopieren van objecten/data zoveel mogelijk te vermijden. Wanneer je meerdere kopies van data bijhoudt, moet je die ook synchronizeren. Dikwijls is er een gedistribueerde lock manager nodig. En, bij een multi-client systeem met een hoog aantal update transacties kan replicatie leiden tot een traag werkend systeem als de data alternerend door andere clienten wordt gewijzigd.

1.3.2. Architectuur

Om objectreplicatie te vermijden gebruikt Ozone een enkele instantie architectuur. Er is slechts één instantie van een database object, dat alleen tot leven komt in de de database server. Dit database object is toegankelijk via Proxy objecten.

Een Proxy object is een vertegenwoordiger van het actuele database object die dichter bij de client staat. Het proxy object bevindt zich in de client applicatie. Een proxy object kan beschouwd worden als een persistentiereferentie. Proxy klassen worden automatisch gegenereerd. vanuit de database klassen.

Zowel de database objecten als de proxies implementeren de publieke interface van de database object. Alle Ozone API methoden geven als resultaat een proxies terug die correspondeert met het eigenlijke database object binnen de database. Dit betekent dat client enkel met proxies te maken krijgt. Dit is geen beperking omdat de proxies dezelfde interface bieden en kunnen gebruikt worden alsof zij het werkelijke database object zijn.

Database objecten in de server worden als een geheel in het geheugen geladen wanneer iemand een methode op het object start. Vermits de client in feite een proxy naar het server object gebruikt, wordt het database object geladen als een method op de proxy wordt gestart. Dit is een belangrijke eigenschap omdat deze ervoor zorgt dat de objecten in de server alleen geladen worden als het nodig is. Als bijvoorbeeld de proxy waarmee je werkt een verzameling bevat, dan zullen de verzameling en de proxies in die verzameling ook beschikbaar zijn in de client maar geen van de corresponderende objecten zullen in de server geladen worden. De object server laadt objects volgens het gebruik in de client van de proxy objecten. Hierdoor wordt de belasting en het geheugenverbuik van de server beperkt. Dit wordt gereasliseerd door enkel de objects te laden als ze werkelijk nodig zijn.

1.4. Ozone documentatie

De volgende uitleg helpen je om te verstaan hoe je ozone gebruikt, wat de mogelijke valkuilen zijn, en waarom bepaalde dingen op een bepaalde manier gedaan worden. are done as they are done. Dit gedeelte bevat geen uitleg over de specificaties van API, die je wel terug vindt in de distributie van ozone.

1.4.1. Verschillende implementaties van OzoneInterface

OzoneInterface definiëert de native API van het Ozone systeem. Hiermee kan je objecten cre;euml;ren en verwijderen, er namen aan toekennen en proxy objecten gebruiken. Deze laatste eigenschap is de grote kracht van Ozone.

Verschillende klassen implementeren de OzoneInterface interface. Er zijn twee situaties waarin de programmeur met deze interface te maken heeft. Eerst, in de client om direct toegang te krijgen tot database functies zoals als OzoneInterface.createObject(). En als tweede, binnen de code van de database klassen (klassen die afgeleid zijn van OzoneCompatible).

Database is de implementatie van OzoneInterface die database objecten als resultaat krijgen uit de org.ozoneDB.OzoneCompatibledatabase methode. De programmeur hoeft nooit objecten van deze klasse te cre;euml;ren.

Alle andere implementaties van OzoneInterface zijn afgeleid van org.ozoneDB.ExternalDatabase. Ze kunnen gebruikt worden door de programmeur om vanuit de client toagang te krijgen tot de database. De verschillende klassen leveren elk een andere toegangsmode tot de database server maar ze implementeren allemaal dezelfde interface. Hierdoor loopt dezelfde code (behalve voor de initializatie van het database object) voor verschillende database types.

org.ozoneDB.RemoteDatabase wordt gebruikt om toegang te krijgen tot een afgelegen database server via een socket verbinding. De server mag lokaal of een andere computer draaien.

org.ozoneDB.LocalDatabase wordt gebruikt om toegang te krijgen tot een database die in dezelfde JVM draait als de client. Hierbij is het wel niet mogelijk om tegelijkertijd aanvragen van verschillende clienten te behandelen, maar deze toegangsmethode is sneller.

org.ozoneDB.ClientCacheDatabase is een mooi idee maar is nog niet klaar en ook niet gedocumenteerd.

1.4.2. Proxy objecten

Een proxy object is een vertegenwoordiger voor het werkelijke database object, binnen de client applicaties en binnen andere database objecten. Een proxy object kan gezien worden als een persistente verwijzing.

Proxy klassen worden vanuit de database klassen gegenereerd. Database objecten zijn persistente objects die enkel binnen de server bestaan. Alle database klassen implementeren de org.ozoneDB.OzoneCompatible interface. Voor de database klassen wordt dikwijls *Impl genoemd. Een externe interface die van org.ozoneDB.OzoneRemote afgeleid is, beschrijft de publieke interface van elk database object. De Ozone Post Processor (OPP) tool genereert de proxies vanuit de database klassen. Proxy klassen worden *Impl_Proxy genoemd.

Het volgende diagramma illustreert de relaties tussen Proxy, de database klassen en de interfaces. Denk eraan dat de programmeur alleen de gemarkeerde klassen hoeft te ontwerpen.

Beide, de database objecten en de proxies, implementeren de publieke interface van het database object. Alle ozone API methodes geven proxies terug for het actuele database object in de database. Zodus, de client heeft enkel met proxies te maken. De proxies leveren wel dezelfde interface en kunnen gebruikt worden alsof zij de werkelijke database objecten zijn.

1.4.3. Logica aan serverzijde

Ozone volgt het OO paradigma waarbij de data en de corresponderende bewerkingen samengoevoegd zijn in objects. Ozone slaat niet alleen de data van de objects op maar voorziet ook een runtime omgeving om de methoden van het database object te kunnen uitvoeren. Na het oproepen van een methode van een proxy worden de parameters gecodeerd en naar de server verzonden waar de corresponderende methode van het werkelijke database objects opgeroepen wordt.

Dit is natuurlijk een bewerking die tijd vraagt. Maar in de meeste gevallen gebeurt dit maar een keer per 'business methode' of 'business transactie'. een voorbeeld: we hebben twee business objecten: UserManager en User. We moeten ze persistent maken. The UserManager houdt de Users in een HashMap met de gebruikersnamen als sleutel. Dit betekent dat zowel UserManager als Users database objecten moeten zijn. UserManager heeft een methode addUser(Name, Age, whatever) die een nieuwe user creëert, het nieuwe object vult met gegevens en het object in de HashMap bijvoegt. Het vullen van het User object met data kan resulteren in meerdere methodeoproepen. Vermits User een database object is, zal het UserManager object alleen maar te maken met proxies van het User database objecten. Dit betekent dat de methodeoproepen afgehandeld worden via proxy objecten. Hierbij zou je kunnen verwachten dat dit traag loopt. Maar het UserManager object zelf is een database object en een proxy oproepen vanuit een ander database object (van binnen de server) is veel sneller (ongeveer 1000 maal) dan het oproepen van een database objectmethode van buiten de server. Daarom is het belangrijk om de de Ozone architectuur in de gaten te houden bij het ontwerpen van een Ozone applicatie!

1.4.4. Transacties

Als default wordt bij elke oproep van een proxy methode aan clientzijde een nieuwe transactie gecreëerd. Deze transaction wordt impliciet afgebroken wanneer de method een exception gooit en correct afgesloten in het andere geval. In het boven vermelde UserManager voorbeeld is dit exact wat we willen. Het creëeren van een nieuw User object, het vullen met data en het bijvoegen in de hashmap moet in één enkele transactie gebeuren. Zo loopt het voorbeeld zo snel mogelijk en is ook correct wat transacties betreft.

Dit betekent dat we, indien mogelijk, geen gebruik maken van expliciete transactie-afscheidingen. Hierdoor ben je verplicht om alle code die in één enkele transactie moet gebeuren, in één database object methode moet onderbrengen.

1.5. Kennismaking met Ozone

1.5.1. Overzicht

Deze tutorial is bedoeld om een eerste indruk van het Ozone systeem te geven. We starten met een heel eenvoudig voorbeeld, dat de belangrijkste eigenschappen van het werken met Ozone toont. Andere, complexere voorbeelden vind je in de 'samples' directory van de Ozone distributie. Je kan deze nalezen om vertrouwd te geraken met de meer geavanceerde concepten van Ozone of als startpunt voor je eigen projecten.

1.5.2. Basisbegrippen

Om objectreplicatie te vermijden gebruikt Ozone een architectuur waarbij slechts één instantie van elk object wordt gemaakt. Dit betekent dat ten alle tijde er slechts één instance van een database object bestaat, dat zich in de database server bevindt. Deze databaseobjecten worden beheerd vanuit een client via Proxy objecten.

Dit ziet er traag uit, nietwaar? Nee, eigenlijk niet. Het is in feite dezelfde wijze zoals EJB werkt - maar dan eenvoudiger te programmeren en sneller in uitvoering. Het feit dat de database objects alleen via Proxy objecten kunnen bereikt worden, betekent niet er voor iedere methodeoproep er een bericht van de de client naar de server heen en weer gaat. Dit is omdat Proxies kunnen gebruikt worden als referenties naar database objecten en dit vanuit een client of vanuit andere database object binnen de server. Dus, de meeste (bijna alle) database methodeoproepen worden vanuit de server gestart en die zijn snel. Nu terug naar de voorbeelden.

Proxy objecten vertegenwoordigen het werkelijke database object - binnen de client applicaties en binnen andere database objecten. Een proxy object gezien worden als een persistente referentie. Proxy klassen worden automatisch genereerd uit de database klassen.

1.5.2.1. Installatie

In de meeste gevallen draait de ozone server in een aparte Java VM. The client applicaties maken een connectie met de server via UNIX domain sockets. Het is evenwel ook mogelijk de ozone server en de client binnen dezelfde Java VM. Het voorbeeld in deze sectie werkt met een aparte server.

Om het voorbeeld in deze sectie te kunnen draaien moet je het Ozone systeem correct installeren en daarna de Ozone server starten. De installatie is beschreven in het INSTALL bestand, die zich bevindt in de Ozone root directory.

Zowel de client als de server moeten toegang hebben tot de database klasses en de genereerde Proxy klassen. De eenvoudigste manier om dit te bereiken is door de client en de server vanuit dezelfde directory te starten waar zich de klassen bevinden.

Om alles correct te laten werken moeten de client applicaties de verschillende *.jar bestanden, die met de Ozone distributie meegeleverd zijn, in hun CLASSPATH hebben. De gemakkelijkste manier is de client applicatie starten met het ojvm command in plaats van java.

1.5.3. De Car interface

In dit voorbeeld willen we gegevens over auto's opslaan. Daarom maken we een klasse Car. Maar denk eraan dat alle database objecten vanuit Proxy objecten beheerd worden. Dus moeten we eerst een interface definiëren voor de Car objecten. Alleen de methoden uit deze interface kunnen gebruikt worden om toegang te krijgen tot het achterliggend database object. Om ervoor te zorgen dat deze interface buiten de Ozone server kan gebruikt worden, maken we ook nog een afleiding van de OzoneRemote interface of Ozone:

      // File: Car.java 
      import org.ozoneDB.OzoneRemote;

      public interface Car extends OzoneRemote
      {
          public void setName( String name );         /*update*/
          public String name();
          public void setYearOfConst( int year );     /*update*/
          public int age();
      }
      

Je ziet dat de commentaar /*update*/ geplaatst is achter de set*() methoden. Helaas is er in Java geen manier om aan te geven dat een methode de toestand van een object wijzigt. Maar Ozone moet weten welke deze methodes zijn omdat deze methods resulteren in een update transactie in plaats van een read transactie. Daarom moet de programmeur expliciet deze methodes markeren. Er zijn verschillende manieren om dit te doen.

  • Commentaar in de interface bron code.

    Put a /*update*/ comment at the end of each method that changes the state of the object.

  • Een patroon voor de namen van de update methodes.

    Geef alle update methodes aan naam volgens een reguliere uitdrukking. Zo een patroon can bijvoorbeeld .*_update zijn. Wanneer je OPP start, moet dit patroon meegegeven worden zodat OPP kan bepalen welke methodes er speciale code moet gegenereerd worden. De Garage interface maakt gebruik van een dergelijk patroon.

  • Ozone Class Descriptor

    Een Ozone Class Descriptor (OCD) is een XML bestand dat meta informatie over de klassen bevat. OPP gebruikt de class descriptor om het lock level van elke transaction te bepalen.

1.5.4. The CarImpl class

De database objecten zullen instanties zijn van de CarImpl klasse. Deze klasse moet de Car interface implementeren. Tevens is deze klasse een uitbreiding van OzoneObject. Hierdoor wordt de klassen een database klasse. Dit is de code voor de CarImpl klasse:

      //File: CarImpl.java 
      import org.ozoneDB.OzoneObject;
      import java.util.*;

      public class CarImpl extends OzoneObject implements Car
      {
          /**
          Set version of the serialized data to make it compatible with
          new class versions.
          */
          final static long serialVersionUID = 1L;
          private String      _name;
          private int         _yearOfConst;
          
          public CarImpl()
          {
              _name = new String( "" );
              _yearOfConst = 0;
          }
          
          public String name()
          {
              return _name;
          }
          
          public void setName( String name )
          {
              _name = name;
          }
          
          public void setYearOfConst( int year )
          {
              _yearOfConst = year;
          }
          
          public int age()
          {
              Calendar cal = Calendar.getInstance();
              return cal.get (Calendar.YEAR) - _yearOfConst;
          }
      }

Nu kunnen we de interface en de klasse compileren. Dat doe je met het volgende commando:

        javac Car.java CarImpl.java

In de laatste stap creëren we een proxy klasse voor CarImpl. Proxy objecten zijn een vervanging voor de database objecten in de applicatie. Hun taak is het de oproepen van methodes voor de database objecten door te geven via een connectie naar de database. Ze hebben dezelfde interface als de werkelijke database objecten en je kan met ze werken alsof ze echte database objects zijn.

Om de proxy klasse voor CarImpl te maken gebruiken we het volgende commando:

opp CarImpl

OPP is de Ozone Post Processor. Het creëert de proxy klasse voor een gegeven database klasse. In ons voorbeeld wordt een bestand met als naam Car_Proxy.class gemaakt.

Merk op dat de Ozone server en OPP beide toegang moeten have tot de klassebestanden van alle ozone klassen van de applicatie. Als je met packages werkt, dan moet je de overeenkomstige directories aan je CLASSPATH toevoegen. In dit voorbeeld worden geen packages gebruikt. Als de ./ directory in je CLASSPATH voorkomt, kan je het complete voorbeeld starten door ozone server en OPP te starten in de directory van dit voorbeeld.

1.5.5. OPP en Proxy objecten

1.5.5.1. Hoe is de samenwerking?

De volgende figuur toont alle klassen en interfaces die nodig zijn voor het Proxy systeem van Ozone. De hogere klassen/interfaces zijn een afleiding van de lagere.

De klassen aan de linkerzijde zijn de server-zijde klassen en de klassen rechts zijn de client-zijde klassen. De remote interface zorgt ervoor dat we de proxy objecten kunnen gebruiken zoals de werkelijke database objecten.

                                 ###########
                         .....>> # OPP     # >>.......
                        .        ###########          .
                       .              ^                .
                      .               ^                 .
      ---------------                 .               ---------------
      *Impl                           .               *Impl_Proxy
      ---------------                 .               ---------------
            |         \       ~~~~~~~~~~~~~~~~~~     /      |
            |          -----  [remote interface]  ---       |
      ---------------         ~~~~~~~~~~~~~~~~~~      ---------------
      OzoneObject                     |               OzoneProxy
      ---------------                 |               ---------------
            |                 ~~~~~~~~~~~~~~~~~~
            |                 OzoneRemote
      ~~~~~~~~~~~~~~~         ~~~~~~~~~~~~~~~~~~
      OzoneCompatible
      ~~~~~~~~~~~~~~~

      ----

      Java interfaces : ~~~~~~~~
      Java classes    : --------
      executable      : ########

Het enige wat de programmeur moet doen, is het schrijven van de code van de*Impl klasse en ervoor zorgen dat alle remote methoden die vanuit de clientzijde tegankelijk moeten zijn ook in the remote interface voorkomen. OPP leest *Impl en de remote interface en genereert the *Impl_Proxy proxy klassen.

1.5.5.2. Stap voor stap

Deze sectie beschrijft alle stappen die je moet doorlopen om een een database klasse te maken. Het is een kort overzicht zonder code of voorbeelden.

  • Schrijf de implementie. Creëer een nieuwe klasse en noem die *Impl (bijvoorbeeld AutoImpl). Deze klasse klass moet afgeleid zijn van OzoneObject.

  • 2.Creëer de remote interface. Wanneer de implementatie van de *Impl klasse afgewerkt is, moet je een Java interface ontwerpen die de remote interface van de *Impl klasse voorstelt. Gebruik een eenvoudige naam voor deze interface (bijvoorbeeld Auto).

  • Markeer alle update methoden. Alle methoden van de remote interface die de interne toestand van het object wijzigen of die een oproep doen van een methode die de interne toestand van hetzelfde object (bijvoorbeeld this.doSomething()) moeten gemarkeerd worden. Voeg de commentaar /*update*/ toe aan het einde van elke regel die een update methode definiëert in de remote interface.

  • Maak de *Impl klasse die de pas gecreëerde remote interface implementeert.

  • Start OPP om de proxy klassen te creëren.

1.5.6. Een eenvoudige client applicatie

De client applicatie creëert een car object, doet er enkele bwerkingen mee en tenslotte verwijdert het. Hier is de code van de client applicatie:

      // File: MyApp.java 
      import org.ozoneDB.*;

      public class MyApp
      {
          
          private static ExternalDatabase     db;

          public static void main( String[] args ) throws Exception {

              if (args.length == 0) {
                  System.out.println( "usage: ojvm MyApp create|delete|print" );
                  System.exit( 1 );
              }
              
              // create and open a new database connection
              db = ExternalDatabase.openDatabase( "ozonedb:remote://localhost:3333" );
              System.out.println( "Connected ..." );
              
              db.reloadClasses();
              
              if (args[0].equals( "create" )) {
                  createCar();
              } else if (args[0].equals( "delete" )) {
                  deleteCar();
              } else {
                  printCar();
              }
              
              db.close();
          }

          public static void createCar() throws Exception {
              // create a new Car object with the name "my_first_car"
              // the return value is Car_proxy, which implements the Car-interface
              Car car = (Car)(db.createObject( CarImpl.class.getName(), 0,
                      "my_first_car" ));
              
              car.setName( "gottfried" );
              car.setYearOfConst( 1957 );
          }

          public static void printCar() throws Exception {
              Car car = (Car)(db.objectForName( "my_first_car" ));
              if (car != null) {
                  System.out.println( "The car " + car.name() + " is " 
                          + car.age() + " years old." );
              } else {
                  System.out.println( "Object my_first_car not found." );
              }
          }
          
          public static void deleteCar() throws Exception {
              Car car = (Car)(db.objectForName( "my_first_car" ));
              if (car != null) {
                  db.deleteObject( car );
              } else {
                  System.out.println( "Object my_first_car not found." );
              }
          }
      }
      

Compileren is eenvoudig:

        javac MyApp.java
      

Maar we moeten eerst de Ozone server starten:

        ozone -d'your_ozone_data_directory'
      

En daarna starten we de kleine applicatie:

        ojvm MyApp create
      

Als eerste stap maakt het programma een connectie met de database op computer "localhost" op poort 3333. Als dat lukt dan krijgen we een "Connected" afdruk op het scherm. De volgende stap is het herladen van de database klassen met reloadClasses(). Dit is nodig nadat we wijzigingen aan onze klassen hebben aangebracht en de Ozone server is niet herstart. Zonder dit herladen zou de Ozone server de oude klasse bytecode in zijn cache bijhouden en zou de recent gewijzigde versie niet gebruiken.

Omdat we de create optie meegeven, creëert het programma nu een nieuw database object van het type CarImpl en geeft het de naam my_first_car.

Merk op dat deze naam niets te maken heeft met de _name klassevariabele of de name() methode in CarImpl. Dit is een database kenmerk en dient om een database object terug op te sporen na het herstarten van het client programma.

De volgnde stap in het programma is het toekennen van de naam "gottfried" en het constructiejaar 1957. Daarna wordt de connectie gesloten en stopt het programma.

Het volgende commando start de applicatie opnieuw:

        ojvm MyApp print
      

De applicatie maakt opnieuw een connectie met de server en we halen de auto op met de objectForName method. Daarna wordt de naam en de leeftijd van de auto afgedrukt. Daarna sluit de connectie.

We verwijderen het car object uit de database met:

        ojvm MyApp delete
      

Na het testen van deze eerste Ozone applicatie kan je de Ozone server stilleggen met een q in het Ozone server terminal venster of met het shutdown commando in de administratie tool.

In de volgende secties zullen we dit voorbeeld uitbreiden door een garage bij te voegen voor de auto's.

1.5.7. De Garage interface en de GarageImpl klasse

We voegen een garage bij in de database. Daarom maken we een GarageImpl klasse en een corresponderende interface. Merk op dat in tegenstelling van de Car interface de update methoden van Garage niet gemarkeerd worden met een speciale commentaar maar dat de naam gevormd is volgens een reguliere uitdrukking. In dit voorbeeldis het patroon .*_update. Dit patroon moet als parameter met OPP meegegeven worden wanneer de proxy klasse gegenereerd wordt.

      // File: Garage.java 
      import org.ozoneDB.OzoneRemote;
      import java.util.*;;

      public interface Garage extends OzoneRemote {

          public void addCar_update( String name, int yearOfConst ) throws Exception;

          public Car carForName( String name );

          public Car removeCar_update( String name );

          public Vector oldtimers();
      }
     

We gebruiken een dictionary als datastructuur voor de garage. Deze dictionary bevat alle car objecten volgens hun name. De volgende stap is duidelijk: we moeten een implementatie van de interface schrijven die we GarageImpl noemen.

      File: GarageImpl.java 
      import org.ozoneDB.OzoneObject;
      import java.util.*;

      public class GarageImpl extends OzoneObject implements Garage {
          
          /**
          Set version of the serialized data to make it compatible with
          new class versions.
          */
          final static long   serialVersionUID = 1L;
          
          private Hashtable   cars;
          
          public GarageImpl() {
              cars = new Hashtable();
          }
          
          public void addCar_update( String name, int yearOfConst ) throws Exception {
              Car car = (Car)database().createObject( CarImpl.class.getName() );
              car.setName( name );
              car.setYearOfConst( yearOfConst );
              cars.put( car.name(), car );
          }
          
          public Car carForName( String name ) {
              return (Car)cars.get( name );
          }
          
          public Car removeCar_update( String name ) {
              return (Car)cars.remove( name );
          }
          
          public Vector oldtimers() {
              Vector result = new Vector();
              for (Enumeration e=cars.elements(); e.hasMoreElements(); ) {
                  Car car = (Car)e.nextElement();
                  if (car.age() >= 20) {
                      result.add( car );
                  }
              }
              return result;
          }
          
          public void onDelete() throws Exception {
              for (Enumeration e=cars.elements(); e.hasMoreElements(); ) {
                  Car car = (Car)e.nextElement();
                  database().deleteObject( car );
              }
          }
      }
     

Het meeste van de bovenstaande code is zelfbeschrijvend. We voegen bij, halen op en verwijderen car objecten door middel van hun naam als identificatie. Het ziet er heel eenvoudig uit, maar denk eraan dat de lijst met car objecten geen objecten van het type CarImpl bevat! De lijst bevat alleen maar proxy objecten van het type Car_proxy. Elk van deze objecten staat in de plaats van een database object van het type CarImpl. Beide types, CarImpl en Car_proxy, implementeren de Car interface en het proxy object heeft hetzelfde gedrag als het corresponderende database object. In Ozone werk je altijd met proxy objecten. De enige uitzondering op deze regel is natuurlijk vanuit het standpunt van een methode van het database object zelf. In dit geval heb je natuurlijk een directe toegang tot het object via de this referentie. kijk even naar de oldtimers() methode. The result in de cars dictionary bevat proxy objecten en zodus is de oproep van de age() methode een RMI oproep.

Tenslotte kijken we naar de onDelete methode. Ieder OzoneObject heeft deze methode and ze wordt automatisch opgeroepen wanneer we het object verwijderen uit de database. Dit is te vergelijken met een destructor in C++. De onDelete() moet alle car objecten verwijderen als we de garage zelf verwijderen. Hiervoor hebben we een connectie naar de database nodig zodat we deleteObject() kunnen oproepen. De methode database() geeft ons de juiste database proxy. Elk OzoneObject heeft deze database-methode en met deze link naar de database kan je database methoden zoals createObject() of deleteObject() oproepen.

Nu moeten we de Garage interface en de GarageImpl klasse en moeten we de proxy klasse genereren.

        javac GarageImpl
        opp -p".*_update" GarageImpl
     

Merk op dat er de -p parameter megegeven wordt. Hiermee wordt het patroon voor de update methodes aangegeven.

1.5.8. De GarageApp applicatie

De applicatie doet vier dingen (afhankelijk van de commandolijnargumenten): creëer een nieuwe garage, verwijder de garage, voeg een nieuwe car bij en print al de oldtimers. Hier is de code:

 // File: GarageApp.java 
 import java.util.*;
 import org.ozoneDB.*;

 public class GarageApp {
     
     public static void main( String[] args ) throws Exception {
         
         if (args.length == 0) {
             System.out.println( "usage: ojvm GarageApp create|delete|oldtimers" );
             System.exit( 1 );
         }
         
         ExternalDatabase db = ExternalDatabase.openDatabase( "ozonedb:remote://localhost:3333" );
         System.out.println( "Connected ..." );
         
         db.reloadClasses();
         
         if (args[0].equals( "create" )) {
             db.createObject( GarageImpl.class.getName(), 0, "my_garage" );
         
         } else if (args[0].equals( "delete" )) {
             Garage garage = (Garage)(db.objectForName( "my_garage" ));
             if (garage != null) {
                 db.deleteObject( garage );
             }

         } else if (args[0].equals( "add" )) {
             Garage garage = (Garage)(db.objectForName( "my_garage" ));
             if (garage != null) {
                 garage.addCar_update( args[1], Integer.parseInt( args[2] ) );
             } else {
                 System.out.println( "Garage object not found!" );
             }

         } else if (args[0].equals( "oldtimer" )) {
             Garage garage = (Garage)(db.objectForName( "my_garage" ));
             if (garage != null) {
                 System.out.println( "Oldtimers:" );
                 Vector oldtimers = garage.oldtimers();
                 for ( int i = 0; i < oldtimers.size();  i++ ) {
                     Car car = (Car)(oldtimers.elementAt( i ));
                     System.out.println( "    Car " + car.name() + " age=" 
                             + car.age() );
                 }
             } else {
                 System.out.println( "Garage object not found!" );
             }
         } else {
             System.out.println( "Unknown parameter: " + args[0] );
         }
         
         db.close();
     }
 }
     

De functionaliet van dit programma is duidelijk. Als we de add optie meegeven, moeten de overige argumenten de naam en het jaar van constructie zijn - hetzelfde geldt voor de overige opties.

Kijk even naar de oldtimer sectie in de main() methode: het resultaat list van de oldtimer() methode bevat proxy objecten. Daarom zijn de oproepen van age() en name() RMI oproepen. Maar we maken deze oproepen naar deze methods van buiten de server; dit betekent dat de methode-oproepen doorheen de socketconnectie gaan. Maar de code ziet er eenvoudig uit. Let wel op dat itereren doorheen collectie van database objecten buiten een database methode in een zeer slechte performantie en consistentieproblemen resulteert In feite worden alle methode-oproepen van biten de Ozone server transacties. Als de methode-oproep terugkeert, wordt een commit van de transactie gedaan, anders krijg je een exceptie en wordt de transactie afgebroken. Je hoeft de transacties niet expliciet te starten en commit uit te voeren Dit betekent dat alle acties van een transactie in een methode gegroepeerd moeten worden. In ons voorbeeld itereren we over een collectie van objecten. Natuurlijk moet dit een transactie zijn omdat we niet willen dat het GarageImpl object veranderd wordt zolang we ermeer bezig zijn.

1.5.9. Samenvatting

Met deze korte tutorial heb je een eerste ideee hoe je met Ozone werkt. Je kunt de voorbeelden verder bekijken. Ozone gebruiken is eenvoudig, als je altijd de basisprincipes volgt. Vooral de betekenis van de Proxy objecten in Ozone is belangrijk. De rest is programmeren zoals gewoonlijk.

1.6. Server Configuratie

De performantie van een Ozone applicatie hangt in grote mate af van de configuratie van de server. Er zijn verschillende parameters die door de administrator ingesteld kunnen worden. Een complete lijst van alle parameters wordt getoond wanneer je het commando ozoneInst zonder opties start.

De parameters kunnen opgegeven worden via ozoneInst of wanneer de server gestart wordt. De algemene vorm is:

        ozone -D<property>=<value>

1.6.1. Heap grootte van de VM

Ozone gebruikt per default een 32MB heap (de werkelijke grootte wordt getoond bij het opstarten) De heapgrootte definiëert de grootte van de object cache van de server. Al het beschikbare geheugen wordt gebruikt voor de object cache.

A very important rule to get good ozone performance is to have enough RAM available to keep the "working set" of the data in the cache. (this is true for all ODBMS I think) Otherwise clusters activation/passivation takes place while performing a transaction which is by far the most time consuming operation. The effect is comparable to OS page swapping.

The heap size of the server can be specified in the command line of the server via the -Xmx<size> command.

1.6.2. B-Tree buffer/cache grootte

Een gerelateerd probleem is de grootte van de centrale b-tree die object ID's omzet in objecten. Vermits de performantie van deze b-tree zeer belangrijk is voor de hele ozone performantie zijn er twee cache niveau's. Het eerste niveau is een kleine direct gemapte cache. Het tweede niveau in de cache houdt de LRU pagina's van de b-tree in het geheugen. De grootte van beide is instelbaar met de volgende parameters:

  • ozoneDB.wizardStore.tableCacheSize

    Het aantal significante bits van een hash waarde voor het eerste niveau van de direct gemapte cache; default = 12 -> 4096 entries.

  • ozoneDB.wizardStore.tableSubtableSize

    Het aantal significante bits voor ëën b-tree level; default = 11 -> 2^11/10 -> ca. 1700 entries per b-tree page.

  • ozoneDB.wizardStore.tableBufferSize

    Het aantal b-tree pagina's in het tweede niveau cache; default = 15 -> 15 * 1700 = 25500 entries.

  • ozoneDB.wizardStore.clusterSizeRatio

  • ozoneDB.wizardStore.compressClusters

  • ozoneDB.wizardStore.clusterSize

  • ozoneDB.port

  • ozoneDB.fileLog / ozoneDB.stdoutLog

1.7. Vraag en antwoord

Q: Hebben alle persistente klassen een lege constructor zonder parameter nodig?

A: Nee. De compiler genereert een default constructor indien nodig.

Q: Wat is het verschil tussen het creëren van objecten van buiten de server en van binnen de server?

A: Het resultaat van beide bewerkingen is hetzelfde. Maar een database object creëren vanuit een ander database object is veel sneller dan van de client. En, indien je terzelfdertijd meerdere objecten creëert, en je doet dit binnen een database methode zullen alle creëer bewerkingen binnen een transactie verlopen (zonder de verplichting om expliciet transactiegrenzen te markeren).

Q: Het schijnt mogelijk te zijn om database objecten binnen andere database objecten te creëren met een oproep van new in plaats van createObject(), maar het gedrag hiervan schijnt fout te zijn. Is dit verondersteld te werken of wordt dit afgeraden?

A: new maakt geen database object. Maar niet alle persistent klassen moeten database klassen zijn. Een Car klasse mag een String variabele name hebben. Dit name object is persistent en wordt in de database opgeslagen maar het is geen database object omdat niemand anders dan dit ene Car object het kan bereiken.

Q: Moeten alle persistente objecten gecreëerd worden met de constructor zonder parameter of kan je ook een andere constructor toepassen?

A: Er is een createObject methode die een andere dan de default constructor oproept. Maar er is nog werk aan omdat het de handtekening van de constructor als string verwacht.

Q: Is de redenering juist dat als je een object creëert met database().createObject(), de constructor van myObject geen andere objecten kan creëren - Ik krijg de melding "object is not yet associated to a database container".

A: Ja, dit is correct. Daarom moet je, ook al ziet dit er niet zo goed uit, een initializatie methode gebruiken.

Q: Kan je uitleggen of de namen bij objecten met naam uniek binnen de database of alleen uniek binnen de klasse moeten zijn?

A: DE namen moeten uniek zijn binnen de database. Objecten met naam zijn startpunten van waaruit je andere objecten kunt bereiken.

Q: Zijn er benchmarks van Ozone?

A: Dee ozone package bevat de OO1 en OO7 benchmarks (directory $OZONE_HOME/samples).

Q: Er staat in de documentation dat het hele object opgeslagen wordt en dat de methode-oproepen naar de objecten in de database verzonden worden. Is dit altijd het geval (dat de methode-oproep plaats vindt in de database)?

A: Ja.

Q: Wordt serializatie gebruikt?

A: Ja.

Q: Wat in verband met schema-evolutie?

A: Methodes en klassevariabelen bijvoegen en verwijderen werkt. Kijk in de jdk documentatie over serializatie en serialVersionUID.

Indien je de database objecten ook nog Externalizable maakt en een goede implementatie maakt van read/writeExternal kan je een redelijk goede schema-evolutie voorzien. Je kan dan een versienummer (niet serialVersionUID !) maken, het wegschrijven met writeExternal, en het testen in readExternal en speciale acties ondernemen bij een bepaalde waarde van het versionnumm. Hiermee kan je de limieten van de normale serializatie schema-evolution vermijden, bijvoorbeeld een klassevariabele verwijderen. De serialVersionUID blijft altijd hetzelfde Hierdoor denkt Java dat de object in de stroom altijd compatibel zijn met de huidige implementatie. Het tweede versienummer is voor jezelf om bij te houden welke versie er geserializeerd is.

Een voorbeeld:

public class TestImpl extends OzoneObject implements Test, Externalizable
{ 

   // remains always 1 
   static long serialVersionUID = 1L; 

   // increase it if you added or removed a member 
   static int subSerialVersionUID = 2L; 

   // is member since version 1 
   private String aMember; 

   // a new member 
   private HashMap bMember; 


   // ... some methods ... 

   public void writeExternal( ObjectOutput out ) throws IOException
   { 
      out.writeInt( subSerialVersionUID ); 
      out.writeObject( aMember ); 
      out.writeObject( bMember ); 
   } 

   public void readExternal( ObjectInput in ) throws IOException
   { 
      int version = in.readInt(); 
      aMember = (String)in.readObject(); 
      if (version == 1)
      {
         // a deleted member from version 1 
         in.readObject(); 
      } 
      if (version > 1)
      { 
         bMember = (HashMap)in.readObject(); 
      } 
   } 
}

Hoofdstuk 2. Grote Ozone voorbeelden

Inhoudsopgave

2.1. Erfenis
2.1.1. Auto
2.1.2. AutoImpl
2.1.3. Bus en BusImpl
2.1.4. Client
2.1.5. Client2
2.2. Lijst
2.2.1. Appl
2.2.2. App2
2.2.3. App3
2.2.4. App4
2.2.5. Database objecten voor Lijst voorbeeld
2.3. Lijst3
2.3.1. Car
2.3.2. CarImpl
2.3.3. Garage
2.3.4. GarageImpl
2.3.5. Lijst
2.3.6. LijstApp
2.3.7. LijstNotify
2.3.8. LijstServer
2.3.9. LijstWatch
2.4. Weersmeting
2.4.1. WeersGegevens
2.4.2. WeersGegevensImpl
2.4.3. Plaats
2.4.4. PlaatsImpl
2.4.5. Soort
2.4.6. SoortImpl
2.4.7. Meting
2.4.8. MetingImpl
2.4.9. Weer2
2.4.10. Weer3
2.4.11. Weer4
2.4.12. Weer5
2.4.13. WeerBis
2.4.14. weer.xml
2.4.15. weer-html.xsl

2.1. Erfenis

Het eerste voorbeeld demonstreert hoe erfenis tussen klassen in een databse kunnen voorkomen. De mogelijkheid om erfenis toe te passen is een voordeel van OO database ten opzichte van de relationele database.

2.1.1. Auto

Als eerste maken we een interface. Deze interface moet afgeleid zijn van OzoneRemote. De functies de erin voor komen, mogen met een update gemarkeerd worden. Hiermee wordt aan de OO database gesignaleerd dat deze functie een object wijzigt.


import org.ozoneDB.*;


public interface Auto extends OzoneRemote
{
   public void setName (String newName);
   public String name();
   public void setAge (Integer newAge) throws Exception; /*update*/
   public int setAge (int newAge) throws Exception; //update
   public Integer age() throws Exception;
   public String naarString();
}

2.1.2. AutoImpl

De tweede klasse is de implementatieklasse, afgeleid van de bovenstaande interface en OzoneObject. In de implementatieklasse mogen datavelden voorkomen.


import org.ozoneDB.*;


public class AutoImpl extends OzoneObject implements Auto
{
   String name = "Ford";
   int age = 0;

   public AutoImpl()
   {
     // System.out.println ("Auto ctor...");
   }
      

   public boolean equals (Object obj)
   {
      Auto auto = (Auto)obj;
      return name.equals (auto.name());
   }
      

   public void setName (String newName)
   {
      name = newName;
   }
   

   public String name()
   {
      return name;
   }
      

   public void setAge (Integer newAge)
   {
      age = newAge.intValue();
   }


   public int setAge (int newAge)
   {
      int ret = age;
      age = newAge;
      return age;
   }
      
   
   public Integer age()
   {
      return new Integer(age);
   }

   public String naarString()
   {
      return toString();
   }
   
   public String toString()
   {
      return super.toString() + "Auto:" + name + ", age " + String.valueOf(age);
   }
}

2.1.3. Bus en BusImpl

Hier volgen de interface en de implementatieklasse


import org.ozoneDB.*;


public interface Bus extends Auto
{
   public String naarString();
}


import org.ozoneDB.*;


public class BusImpl extends AutoImpl implements Bus
{
   
   int capacity = 12;
   
   public BusImpl()
   {
   }

   public String naarString()
   {
      return toString();
   }

   public String toString()
   {
      return super.toString() + "Bus:" + name + ", capacity: " +  capacity;
   }
}

2.1.4. Client

Dit is de clientzijde:


import org.ozoneDB.*;


class Client extends Object
{
    
   public static void main (String[] args) throws Exception
   {
       RemoteDatabase db = new RemoteDatabase();
       db.open ("localhost", 3333);
       
       // herlaadt de klassen in de ozone server
       db.reloadClasses();
       System.out.println ("connected...");

       // maak een bus object
       Bus bus = (Bus)db.createObject (BusImpl.class.getName());
       System.out.println ("bus:" + bus.naarString());

       // en verwijder onmiddellijk
       db.deleteObject (bus);

       // zoek een Auto object met naam "auto"
       Auto auto = (Auto)db.objectForName ("auto");
       if (auto == null)
       {
           // niet gevonden, creeer dan
           auto = (Auto)db.createObject (AutoImpl.class.getName(), 
                         OzoneInterface.Public, "auto");
       }
       System.out.println ("auto:" + auto.naarString());

       // zet de leeftijd op 3
       auto.setAge (new Integer(3));
       System.out.println ("auto.setAge (3)");

       // maak een kopie, dit is een nieuw object
       Auto copy = (Auto)db.copyObject (auto);
       copy.setAge (new Integer(100));
       System.out.println ("\nauto:" + auto.naarString());
       System.out.println ("copy:" + copy.naarString());

       // verwijder kopie
       db.deleteObject (copy);
          
       db.close();
       System.out.println ("deconnected...");
       }
   }

2.1.5. Client2

Dit is een tweede clientprogramma


import org.ozoneDB.*;


class Client2 extends Object
{
    
   public static void main (String[] args) throws Exception
   {
       RemoteDatabase db = new RemoteDatabase();
       db.open ("localhost", 3333);
       
       db.reloadClasses();
       System.out.println ("connected...");

       // maan een bus
       String naam = BusImpl.class.getName();
       Bus bus = (Bus)db.createObject (naam);
       System.out.println ("bus:" + bus);

       // maak een auto
       Auto auto = (Auto)db.createObject (AutoImpl.class.getName());
       System.out.println ("auto:" + auto.naarString());

       // maak een tweede ato
       Auto auto2 = (Auto)db.createObject (BusImpl.class.getName());
       System.out.println ("auto:" + auto2.naarString());

       // deze functie geeft een lijst van alle Auto (en afgeleide) objecten
       OzoneProxy[] lijst = db.objectsOfClass("AutoImpl");
       if (lijst == null)
       {
          System.out.println("geen objecten gevonden");
       }
       else
       {
          for (int i=0; i<lijst.length; i++)
          {
             Auto a = (Auto) lijst[i];
             System.out.println("+ auto: " + a.naarString());
          }
       }
       db.close();
       System.out.println ("deconnected...");
   }
}

2.2. Lijst

Dit voorbeeld toont een Garage object met daaraan meerdere Car objecten gekoppeld. Met de toepassing App1 kan je een Car object maken, tonen en verwijderen.

2.2.1. Appl


import org.ozoneDB.*;

public class App1 extends Object
{

    public static void main (String[] args)
    {
        if (args.length == 0)
        {
            System.out.println ("usage: java MyApp create | delete | print");
            System.exit (1);
        }

        // create a new database proxy
        RemoteDatabase db = new RemoteDatabase();

        try
        {
            //  open the connection on localhost at port 3333
            db.open ("localhost", 3333);
            System.out.println ("Connected ...");

            // reload our database classes if changed them
            db.reloadClasses();
            
            if (args[0].equals ("create"))
            {
                // create a new Car object with the name "my_first_car"
                // the return value is Car_proxy, which implements the Car-interface
                Car car = (Car)db.createObject (CarImpl.class.getName(), 0, "my_first_car");

                // set the name and the year of construction of the car
                car.setName ("gottfried");
                car.setYearOfConst (new Integer (1957));
            }
            else if (args[0].equals ("print"))
            {
                // get the car
                Car car = (Car)db.objectForName ("my_first_car");
                // print the name and the age of the car
                if (car != null)
                    System.out.println ("The car " + car.name() + " is " + car.age() + " years old.");
                else
                    System.out.println ("Object my_first_car not found");
            }
            else if (args[0].equals ("delete"))
            {
                // get the car
                Car car = (Car)db.objectForName ("my_first_car");
                // delete the car
                if (car != null)
                    db.deleteObject (car);
                else
                    System.out.println ("Object my_first_car not found");
            }

            db.close();
        }
        catch (Exception e)
        {
            System.out.println (e);
            try
            {
                db.close();
            }
            catch (Exception ex)
            {
                System.out.println (ex);
            }
         }
      }
}

2.2.2. App2

Met de toepassing App2 worden de Garage en enkele Car objecten gemaakt. De Car objecten worden aan de Garage gekoppeld.


import org.ozoneDB.DxLib.*;
import org.ozoneDB.*;

public class App2 extends Object
{
   public static void main (String[] args)
   {
      RemoteDatabase db = new RemoteDatabase();
        
      try
      {
         // open the connection on localhost at port 3333
         db.open ("localhost", 3333);
         System.out.println ("Connected ...");

         // reload our database classes if we changed them
         db.reloadClasses();


         Garage garage = (Garage)db.objectForName ("my_garage");
         if (garage == null)
         {
            garage = (Garage) db.createObject (GarageImpl.class.getName(), 0, "my_garage");
         }

         Car car = (Car)db.createObject (CarImpl.class.getName(), 0, null);
         car.setName ("VW");
         car.setYearOfConst (new Integer(1965));
         garage.addCar_update (car);

         car = (Car)db.createObject (CarImpl.class.getName(), 0, null);
         car.setName ("Opel");
         car.setYearOfConst (new Integer(1964));
         garage.addCar_update (car);

         car = (Car)db.createObject (CarImpl.class.getName(), 0, null);
         car.setName ("Ford");
         car.setYearOfConst (new Integer(1967));
         garage.addCar_update (car);

         // close the connection
         db.close();
        }
        catch (Exception e)
        {
           System.out.println (e);
           e.printStackTrace();
            try
            {
                db.close();
            }
            catch (Exception ex)
            {
                System.out.println (ex);
            }
       }
    }
}

2.2.3. App3

Met App3 wordt de Garage met zijn Car objecten getoond.


import org.ozoneDB.DxLib.*;
import org.ozoneDB.*;

public class App3 extends Object
{
   public static void main (String[] args)
   {
      RemoteDatabase db = new RemoteDatabase();
        
      try
      {
         // open the connection on localhost at port 3333
         db.open ("localhost", 3333);
         System.out.println ("Connected ...");

         // reload our database classes if we changed them
         db.reloadClasses();

         // zoek de Garage op naam
         Garage garage = (Garage)db.objectForName ("my_garage");
         if (garage == null)
         {
            System.out.println("my_garage niet gevonden");
            return;
         }

         // doorloop alle Car objecten
         Car car;
         DxBag oldtimers = garage.oldtimers();
         DxIterator it = oldtimers.iterator();
         while (it.next() != null)
         {
            car = (Car)it.object();
            System.out.println ("The car " + car.name() + " is " + car.age()
             + " years old.");
         }

            // close the connection
         db.close();

      }
      catch (Exception e)
      {
         System.out.println (e);
         e.printStackTrace();
         try
         {
            db.close();
         }
         catch (Exception ex)
         {
            System.out.println (ex);
         }
      }
   }
}

2.2.4. App4

App4 doet hetzelfde als App3, maar dan met een grafische user interface.


import java.awt.*;
import java.awt.event.*;

import org.ozoneDB.DxLib.*;
import org.ozoneDB.*;

public class App4 extends Frame
{
   private Button voegbij;
   private List   lijst;
   private RemoteDatabase db;
   private Garage garage;


   // deze functie start de databaseconnectie
   public void dbbegin()
   {
      db = new RemoteDatabase();
        
      try
      {
         // open the connection on localhost at port 3333
         db.open ("localhost", 3333);
         System.out.println ("Connected ...");

         // reload our database classes if we changed them
         db.reloadClasses();

         garage = (Garage)db.objectForName ("my_garage");
         if (garage == null)
         {
            System.out.println("my_garage niet gevonden");
            System.exit(0);
         }

         Car car;
         DxBag oldtimers = garage.oldtimers();
         DxIterator it = oldtimers.iterator();
         while (it.next() != null)
         {
            car = (Car)it.object();
            System.out.println ("The car " + car.name() + " is " + car.age() 
               + " years old.");

            // voeg de naam van de Car bij in de listbox
            lijst.addItem(car.name());
         }
      }
      catch (Exception e)
      {
         System.out.println (e);
         e.printStackTrace();
         try
         {
            db.close();
         }
         catch (Exception ex)
         {
            System.out.println (ex);
         }
      }
   }

   // beeindig de databaseconnectie
   public void dbeinde()
   {
      try
      {
         System.out.println("close()");
         db.close();
      }
      catch (Exception e)
      {
         System.out.println (e);
         e.printStackTrace();
         try
         {
            db.close();
         }
         catch (Exception ex)
         {
            System.out.println (ex);
         }
      }
   }

   // voeg een Car bij in de Garage
   public void dbvoegbij(String naam)
   {
      System.out.println("voegbij " + naam);

      try
      {
         Car car = (Car)db.createObject (CarImpl.class.getName(), 0, null);
         car.setName (naam);
         car.setYearOfConst (new Integer(1965));
         garage.addCar_update (car);

         // de Car wordt zowel in de listbox als in de garage bijgevoegd
         lijst.addItem(car.name());
      }
      catch(Exception e)
      {
         System.out.println("Exception " +e);
      }
   }


   public App4(String titel)
   {
      super(titel);

      this.addWindowListener(new WindowAdapter()
      {
         public void windowClosing(WindowEvent e)
         {
            dbeinde();
            System.exit(0);
         }
      });

      setLayout(new BorderLayout(10,10));

      voegbij = new Button("voegbij");
      add(voegbij, "North");

      ActionListener buttonlistener = new ActionListener()
      {
         public void actionPerformed(ActionEvent e)
         {
            dbvoegbij("Renault");
         }
      };
      voegbij.addActionListener(buttonlistener);

      lijst = new List(8);
      add(lijst, "Center");

      dbbegin();
   }

   public static void main (String[] args)
   {
      Frame f =  new App4("Titel");
      f.pack();
      f.show();
   }

}

2.2.5. Database objecten voor Lijst voorbeeld

Dit zijn de databaseobjecten:


import org.ozoneDB.OzoneRemote;

//define our Car-interface

public interface Car extends OzoneRemote
{
    // two methods to set/get the name of a car
    public void setName (String name); /*update*/
    public String name();

    // a method to get the year of construction
    public void setYearOfConst (Integer year); //update

    // a method to get the age of a car
    public Integer age();
}






import org.ozoneDB.OzoneObject;
import java.util.*;

public class CarImpl extends OzoneObject implements Car
{
    // some members
    String _name;
    int _yearOfConst;

    // a constructor without arguments
    public CarImpl() {
        _name = new String ("");
        _yearOfConst = 0;
        }

    // implement the interface methods
    public String name() {
        return _name;
        }

    public void setName (String name) {
        _name = name;
        }

    public void setYearOfConst (Integer year) {
        _yearOfConst = year.intValue();
        }

    public Integer age() {
        Calendar cal = Calendar.getInstance();
        return new Integer (cal.get (Calendar.YEAR) - _yearOfConst);
        }
    }
 



import org.ozoneDB.OzoneRemote;
import org.ozoneDB.DxLib.DxBag;

public interface Garage extends OzoneRemote
{
    // some methods to add, get and remove cars
    public void addCar_update (Car newCar);
    public Car carForName (String name);
    public Car removeCar_update (String name);

    // returns all cars which are older than 20 years
    public DxBag oldtimers();
}


import org.ozoneDB.OzoneObject;
import org.ozoneDB.DxLib.*;


/** */
public class GarageImpl extends OzoneObject implements Garage
{

    /** the garage dictionary */
   DxMap theCars = new DxHashMap();


    /** the constructor without arguments */
   public GarageImpl()
   {
   }


   /** */
   public void addCar_update (Car newCar)
   {
      // we have to convert name() to a DxString because the method
      // needs a DxObject as the key
      theCars.addForKey (newCar, new DxString (newCar.name()));
   }




    /** zoek een Car op naam in de hashmap */
   public Car carForName (String name)
   {
      return (Car)theCars.elementForKey (new DxString (name));
   }

    
    /** */
   public Car removeCar_update (String name)
   {
      return (Car)theCars.removeForKey (new DxString (name));
   }

    
    /** */
    public DxBag oldtimers()
    {
        // build the result list
        DxBag result = new DxArrayBag();
        // build the iterator for our car list
        Car car;
        DxIterator it = theCars.iterator();
        while (it.next() != null)
        {
            car = (Car)it.object();
            // is the car an oldtimer ?
            if (car.age().intValue() >= 20)
                result.add (car);
        }

        return result;
    }
}

2.3. Lijst3

Deze toepassing is een meerdere clienten, 1 server en 1 database toepassing. Elke client ziet dezelfde lijst. Er is een invoerveld voorzien voor de naam van een nieuwe Car. Met een knop kan een nieuwe Car aan de lijst toegevoegd worden. Deze nieuwe Car wordt aangemeld bij de server. Deze voegt de nieuwe Car bij in de database en in de lijsten van de overige clienten.

2.3.1. Car


//import the OzoneRemote-Interface
import org.ozoneDB.OzoneRemote;

//define our Car-interface

public interface Car extends OzoneRemote
{
    // two methods to set/get the name of a car
    public void setName (String name); /*update*/
    public String name();

    // a method to get the year of construction
    public void setYearOfConst (Integer year); //update

    // a method to get the age of a car
    public Integer age();
}

2.3.2. CarImpl


//set your proper package name here (e.g. ozone.doc.tutorial) and
//add this to your CLASSPATH variable

//package doc.tutorial;

import org.ozoneDB.OzoneObject;
import java.util.*;

public class CarImpl extends OzoneObject implements Car {
    // some members
    String _name;
    int _yearOfConst;

    // a constructor without arguments
    public CarImpl() {
        _name = new String ("");
        _yearOfConst = 0;
        }

    // implement the interface methods
    public String name() {
        return _name;
        }

    public void setName (String name) {
        _name = name;
        }

    public void setYearOfConst (Integer year) {
        _yearOfConst = year.intValue();
        }

    public Integer age() {
        Calendar cal = Calendar.getInstance();
        return new Integer (cal.get (Calendar.YEAR) - _yearOfConst);
        }
    }

2.3.3. Garage


import org.ozoneDB.OzoneRemote;
import org.ozoneDB.DxLib.DxBag;

public interface Garage extends OzoneRemote
{
    // some methods to add, get and remove cars
    public void addCar_update (Car newCar);
    public Car carForName (String name);
    public Car removeCar_update (String name);

    // returns all cars which are older than 20 years
    public DxBag oldtimers();

    // a destructor method
    public void done();
}

2.3.4. GarageImpl


import org.ozoneDB.OzoneObject;
import org.ozoneDB.DxLib.*;


/** */
public class GarageImpl extends OzoneObject implements Garage
{

    /** the garage dictionary */
   DxMap theCars = new DxHashMap();


    /** the constructor without arguments */
   public GarageImpl()
   {
   }


   /** */
   public void addCar_update (Car newCar)
   {
      // we have to convert name() to a DxString because the method
      // needs a DxObject as the key
      theCars.addForKey (newCar, new DxString (newCar.name()));
   }


    /** */
   public Car carForName (String name)
   {
      return (Car)theCars.elementForKey (new DxString (name));
   }

    
    /** */
   public Car removeCar_update (String name)
   {
      return (Car)theCars.removeForKey (new DxString (name));
   }

    
    /** */
    public DxBag oldtimers()
    {
        // build the result list
        DxBag result = new DxArrayBag();
        // build the iterator for our car list
        Car car;
        DxIterator it = theCars.iterator();
        while (it.next() != null)
        {
            car = (Car)it.object();
            // is the car an oldtimer ?
            if (car.age().intValue() >= 20)
                result.add (car);
        }

        return result;
    }

    
    /** */
    public void done()
    {
        // when we delete the garage we must also delete the cars
        DxIterator it = theCars.iterator();
        try
        {
            while (it.next() != null)
                database().deleteObject ((Car)it.object());
        }
        catch (Exception e)
        {
            System.out.println (e);
        }
    }
}

2.3.5. Lijst


import java.applet.Applet;
import java.awt.*;
import java.net.URL;
import java.rmi.*;
import java.rmi.server.*;
import java.util.*;

// Lijst is een afgeleide van List. De Lijst staat in verbinding
// met de server

public class Lijst extends List implements LijstNotify, java.io.Serializable
{
   private LijstWatch lijstWatch = null;  // verwijzing naar de server

   public Lijst(int rows, String lijstnaam)
   {
      super(rows);

      try
      {
	      // exporteer de lijst als een remote object
	      //System.out.println("Lijst contr: exporteer remote object");
	      UnicastRemoteObject.exportObject(this);
	    
	      // zoek LijstServer server
	      String serverName = "//localhost:/" + lijstnaam;
	    
         System.out.println("Lijst.constr: zoek server " + serverName);
         lijstWatch = (LijstWatch) Naming.lookup(lijstnaam);

         // registreer om lijst updates te ontvangen
         System.out.println("Lijst.constr: watch ");
         if (lijstWatch != null)
         {
            lijstWatch.watch(this);
	         System.out.println("Lijst.constr: registratie gedaan");
	      }
	      else
	      {
	         System.out.println("Lijst.constr: server object niet gevonden");
	      }
      }
      catch (Exception e)
      {
	      // fatal error
         System.out.println("exception 1: " + e.getMessage());
         e.printStackTrace();
         return;
      }
   }


   public void einde()
   {
      if (lijstWatch != null)
      {
         System.out.println("Lijst.einde()");
         try
         {
		      lijstWatch.cancel(this);
         }
         catch (Exception e)
         {
            // eat exception
	      }
	   }
   }

   // deze functie wordt door de server gestart
   public void update(String naam)
   {
	   super.addItem(naam);
   }


   public void addItem(String naam)
   {
      if (lijstWatch != null)
      {
         try
         {
            // voeg de naam bij in de server
            lijstWatch.voegbij(this, naam);
         }
         catch(Exception e)
         {
	         System.out.println("add exception: " + e.getMessage());
	         e.printStackTrace();
         }
      }
   }
}

2.3.6. LijstApp


// dit is de clientapplicatie

import java.awt.*;
import java.awt.event.*;


public class LijstApp extends Frame
{
   private Button    voegbij;
   private Lijst     lijst;
   private TextField invoer;


   public LijstApp(String titel)
   {
      super(titel);

      this.addWindowListener(new WindowAdapter()
      {
         public void windowClosing(WindowEvent e)
         {
            if (lijst != null)
            {
               lijst.einde();
            }
            System.exit(0);
         }
      });

      setLayout(new BorderLayout(10,10));

      voegbij = new Button("voegbij");
      add(voegbij, "North");

      ActionListener buttonlistener = new ActionListener()
      {
         public void actionPerformed(ActionEvent e)
         {
            lijst.addItem(invoer.getText());
         }
      };
      voegbij.addActionListener(buttonlistener);

      lijst = new Lijst(8, "my_garage");
      add(lijst, "Center");

      invoer = new TextField(40);
      add(invoer, "South");
      //dbbegin();
   }

   public static void main (String[] args)
   {
      Frame f =  new LijstApp("Titel");
      f.pack();
      f.show();
   }

}

2.3.7. LijstNotify


import java.rmi.*;

public interface LijstNotify extends java.rmi.Remote
{
    void update (String naam) throws RemoteException;
}

2.3.8. LijstServer


import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.LocateRegistry;
import java.util.*;

import org.ozoneDB.DxLib.*;
import org.ozoneDB.*;


public class LijstServer extends UnicastRemoteObject implements LijstWatch
{
   private Hashtable notifyTable = new Hashtable();
   private RemoteDatabase db;
   private Garage garage;

   public LijstServer() throws RemoteException
   {
      dbbegin();
   }

   public synchronized void watch (LijstNotify obj)
   {
      System.out.println("LijstServer.watch");

      String s = (String) notifyTable.get(obj);

      // register interested party...
      if (s == null)
      {
         notifyTable.put(obj, new String("") );
      }
      System.out.println("LijstNotify in hash geplaatst");

      // doorloop alle Car's en voegbij bij client's

      try
      {
      Car car;
      DxBag oldtimers = garage.oldtimers();
      DxIterator it = oldtimers.iterator();
      while (it.next() != null)
      {
         car = (Car)it.object();
         if (car != null)
         {
            System.out.println ("The car " + car.name() + " is " + car.age() + " years old.");
            //voegbij(null, car.name());
            obj.update(car.name());

         }
         else
         {
            System.out.println("car null");
         }
      }
      }
      catch(Exception e)
      {
         System.out.println("LijstServer.watch: exc " + e.getMessage());
         e.printStackTrace();
      }
      System.out.println("einde watch()");

   }


   public synchronized void cancel(LijstNotify obj)
   {
      notifyTable.remove(obj);
   }


   public synchronized void voegbij(LijstNotify obj, String naam)
   {
       // enumerate through each watcher...
      Enumeration enum = notifyTable.keys();
      while (enum.hasMoreElements())
      {
         try
         {
            LijstNotify ob = (LijstNotify)enum.nextElement();
            ob.update(naam);
         }
         catch (RemoteException e)
         {
            // can't reach watcher; cancel notification request
            System.out.println("LijstServer.run: exception");
            e.printStackTrace();
         }
      }
   }

   public static void main(String args[])
   {
      // Create and install the security manager
      System.setSecurityManager(new RMISecurityManager());

      try
      {
         System.out.println("LijstServer.main: creating server");
         LijstServer server = new LijstServer();
         System.out.println("LijstServer.main: binding server ");
         Naming.rebind("my_garage", server);
         System.out.println("LijstServer.main: done");
      }
      catch (Exception e)
      {
         System.out.println("LijstServer.main: an exception occurred: " +
		       e.getMessage());
         e.printStackTrace();
      }
   }

   // database functies

   public void dbbegin()
   {
      db = new RemoteDatabase();

      try
      {
         // open the connection on localhost at port 3333
         db.open ("localhost", 3333);
         System.out.println ("Connected ...");

         // reload our database classes if we changed them
         db.reloadClasses();

         garage = (Garage)db.objectForName ("my_garage");
         if (garage == null)
         {
            System.out.println("my_garage niet gevonden");
            System.exit(0);
         }
      }
      catch (Exception e)
      {
         System.out.println (e);
         e.printStackTrace();
         try
         {
            db.close();
         }
         catch (Exception ex)
         {
            System.out.println (ex);
         }
      }
   }

   public void dbeinde()
   {
      try
      {
         System.out.println("close()");
         db.close();
      }
      catch (Exception e)
      {
         System.out.println (e);
         e.printStackTrace();
         try
         {
            db.close();
         }
         catch (Exception ex)
         {
            System.out.println (ex);
         }
      }
   }

   public void dbvoegbij(String naam)
   {
      System.out.println("voegbij " + naam);

      try
      {
         Car car = (Car)db.createObject (CarImpl.class.getName(), 0, null);
         car.setName (naam);
         car.setYearOfConst (new Integer(1965));
         garage.addCar_update (car);
      }
      catch(Exception e)
      {
         System.out.println("Exception " +e);
      }
   }

}

2.3.9. LijstWatch


import java.rmi.*;

public interface LijstWatch extends java.rmi.Remote
{

    void watch(LijstNotify obj) throws RemoteException;
    void cancel(LijstNotify obj) throws RemoteException;
    void voegbij(LijstNotify obj, String naam) throws RemoteException;
}

2.4. Weersmeting

Dit is een ozonetoepassing met 4 klassen in de database:

WeersGegevens toegangsobject

Plaats een plaats waar metingen gebeuren, bv. Diepenbeek

Soort soort meting: zonneintensiteit, windsnelheid, ...

Meting de meetwaarde zelf, waarde en datum

2.4.1. WeersGegevens


package be.khlim.weersmeting;

import org.ozoneDB.OzoneRemote;
import org.ozoneDB.DxLib.DxBag;

public interface WeersGegevens extends OzoneRemote
{
    public void addPlaats(Plaats pl); //update
    public Plaats plaatsForName(String name);
    public Plaats removePlaats(String name); //update

    public DxBag allePlaatsen();
}

2.4.2. WeersGegevensImpl


package be.khlim.weersmeting;

import org.ozoneDB.OzoneObject;
import org.ozoneDB.DxLib.*;


/** */
public class WeersGegevensImpl extends OzoneObject implements WeersGegevens
{
   DxMap plaatsen = new DxHashMap();

   public WeersGegevensImpl()
   {
   }

   public void addPlaats(Plaats pl)
   {
      // we have to convert name() to a DxString because the method
      // needs a DxObject as the key
      plaatsen.addForKey (pl, new DxString (pl.naam()));
   }

   public Plaats plaatsForName(String name)
   {
      return (Plaats) plaatsen.elementForKey (new DxString (name));
   }

   public Plaats removePlaats(String name)
   {
      return (Plaats)plaatsen.removeForKey (new DxString (name));
   }

    
   public DxBag allePlaatsen()
   {
       // build the result list
       DxBag result = new DxArrayBag();
       // build the iterator for our plaats list
       Plaats pl;
       DxIterator it = plaatsen.iterator();
       while (it.next() != null)
       {
           pl = (Plaats)it.object();
           result.add (pl);
       }
       return result;
   }
}

2.4.3. Plaats


package be.khlim.weersmeting;


import org.ozoneDB.OzoneRemote;
import org.ozoneDB.DxLib.*;


public interface Plaats extends OzoneRemote
{
   public void setNaam (String naam); /*update*/
   public String naam();

   public void addSoort(Soort pl); //update
   public Soort soortForName(String name);
   public Soort removeSoort(String name); //update

   public DxMap alleSoorten();
}

2.4.4. PlaatsImpl


package be.khlim.weersmeting;

import org.ozoneDB.OzoneObject;
import org.ozoneDB.*;
import org.ozoneDB.DxLib.*;
import java.util.*;

public class PlaatsImpl extends OzoneObject implements Plaats
{
   String  _naam;
   DxMap   _soorten;

   public PlaatsImpl()
   {
      _naam = new String ("");
      _soorten = new DxHashMap();
   }

   public String naam()
   {
      return _naam;
   }

   public void setNaam (String naam)
   {
      _naam = naam;
   }


   public void addSoort(Soort so)
   {
      // we have to convert name() to a DxString because the method
      // needs a DxObject as the key
      _soorten.addForKey (so, new DxString (so.naam()));
   }

   public Soort soortForName(String name)
   {
      return (Soort) _soorten.elementForKey (new DxString (name));
   }

   public Soort removeSoort(String name)
   {
      return (Soort)_soorten.removeForKey (new DxString (name));
   }


   public DxMap alleSoorten()
   {
      return _soorten;
   }
/*
   public Integer age()
   {
      Calendar cal = Calendar.getInstance();
      return new Integer (cal.get (Calendar.YEAR) - _yearOfConst);
   }
 */
}

2.4.5. Soort


package be.khlim.weersmeting;


import org.ozoneDB.OzoneRemote;
import org.ozoneDB.DxLib.*;


public interface Soort extends OzoneRemote
{
    // two methods to set/get the name of a car
   public void setNaam (String naam); //update
   public String naam();

   public void addMeting(Meting m); //update
   public DxBag alleMetingen();
   public double maxwaarde();
   public double minwaarde();
}

2.4.6. SoortImpl


//set your proper package name here (e.g. ozone.doc.tutorial) and
//add this to your CLASSPATH variable

package be.khlim.weersmeting;

import org.ozoneDB.OzoneObject;
import org.ozoneDB.DxLib.*;
import org.ozoneDB.*;
import java.util.*;

public class SoortImpl extends OzoneObject implements Soort
{
   String  _naam;
   double  _minwaarde;
   double  _maxwaarde;
   DxBag   _metingen;

   public SoortImpl()
   {
      _naam = new String ("");
      _metingen = new DxArrayBag();
   }

   public String naam()
   {
      return _naam;
   }

   public void setNaam (String naam)
   {
      _naam = naam;
   }

   public double minwaarde()
   {
      return _minwaarde;
   }

   public double maxwaarde()
   {
      return _maxwaarde;
   }

   public void addMeting(Meting m)
   {
      _metingen.addBack(m);
   }

   public DxBag alleMetingen()
   {
      return _metingen;
   }
/*
   public Integer age()
   {
      Calendar cal = Calendar.getInstance();
      return new Integer (cal.get (Calendar.YEAR) - _yearOfConst);
   }
 */
}

2.4.7. Meting


package be.khlim.weersmeting;


import java.util.Date;
import org.ozoneDB.OzoneRemote;


public interface Meting extends OzoneRemote
{
   public void setDatum(Date dt); //update
   public void setWaarde(double w); //update
   public Date datum();
   public double waarde();
}

2.4.8. MetingImpl


//set your proper package name here (e.g. ozone.doc.tutorial) and
//add this to your CLASSPATH variable

package be.khlim.weersmeting;

import org.ozoneDB.OzoneObject;
import org.ozoneDB.*;
import java.util.*;

public class MetingImpl extends OzoneObject implements Meting
{
   double  _waarde;
   Date    _datum;

   public MetingImpl()
   {
      _waarde = 0.0;
      _datum  = null;
   }

   public double waarde()
   {
      return _waarde;
   }

   public Date datum()
   {
      return _datum;
   }

   public void setWaarde(double w)
   {
      _waarde = w;
   }

   public void setDatum(Date d)
   {
      _datum = d;
   }
}

2.4.9. Weer2


import org.ozoneDB.DxLib.*;
import org.ozoneDB.*;
import be.khlim.weersmeting.*;

public class Weer2 extends Object
{
   public static void main (String[] args)
   {
      RemoteDatabase db = new RemoteDatabase();
        
      try
      {
         // open the connection on localhost at port 3333
         db.open ("localhost", 3333);
         System.out.println ("Connected ...");

         // reload our database classes if we changed them
         db.reloadClasses();


         WeersGegevens weer = (WeersGegevens)db.objectForName ("weer");
         if (weer == null)
         {
            weer = (WeersGegevens) db.createObject (WeersGegevensImpl.class.getName(), 0, "weer");
         }

         Plaats pl = (Plaats) db.createObject (PlaatsImpl.class.getName(), 0, null);
         pl.setNaam ("Diepenbeek");
         weer.addPlaats(pl);


         // close the connection
         db.close();

        }
        catch (Exception e)
        {
           System.out.println (e);
           e.printStackTrace();
            try
            {
                db.close();
            }
            catch (Exception ex)
            {
                System.out.println (ex);
            }
       }
    }
}

2.4.10. Weer3


import org.ozoneDB.DxLib.*;
import org.ozoneDB.*;
import be.khlim.weersmeting.*;

public class Weer3 extends Object
{
   public static void main (String[] args)
   {
      RemoteDatabase db = new RemoteDatabase();
        
      try
      {
         // open the connection on localhost at port 3333
         db.open ("localhost", 3333);
         System.out.println ("Connected ...");

         // reload our database classes if we changed them
         db.reloadClasses();

         WeersGegevens weer = (WeersGegevens)db.objectForName ("weer");
         if (weer == null)
         {
            System.out.println("weer niet gevonden");
            return;
         }

         Plaats pl;
         DxBag alles = weer.allePlaatsen();
         DxIterator it = alles.iterator();
         while (it.next() != null)
         {
            pl = (Plaats) it.object();
            System.out.println ("Plaats " + pl.naam());
         }

            // close the connection
         db.close();

      }
      catch (Exception e)
      {
         System.out.println (e);
         e.printStackTrace();
         try
         {
            db.close();
         }
         catch (Exception ex)
         {
            System.out.println (ex);
         }
      }
   }
}

2.4.11. Weer4


import java.util.*;

import org.ozoneDB.DxLib.*;
import org.ozoneDB.*;
import be.khlim.weersmeting.*;

public class Weer4 extends Object
{
   public static void main (String[] args)
   {
      RemoteDatabase db = new RemoteDatabase();
        
      try
      {
         // open the connection on localhost at port 3333
         db.open ("localhost", 3333);
         System.out.println ("Connected ...");

         // reload our database classes if we changed them
         db.reloadClasses();

         WeersGegevens weer = (WeersGegevens)db.objectForName ("weer");
         if (weer == null)
         {
            System.out.println("weer niet gevonden");
            return;
         }

         Plaats pl = weer.plaatsForName("Diepenbeek");
         if (pl != null)
         {
            System.out.println ("Plaats " + pl.naam());

            // haal soort zonneintensiteit
            Soort so = pl.soortForName("zonneintensiteit");
            if (so != null)
            {
               System.out.println ("Soort " + so.naam());
            }
            else
            {
               System.out.println ("Soort niet gevonden");

               so = (Soort) db.createObject (SoortImpl.class.getName());
               so.setNaam ("zonneintensiteit");
               pl.addSoort(so);
            }

            // soort is gevonden
            // voeg Meting objecten bij
            for (int i=0; i< 20; i++)
            {
               Meting m = (Meting) db.createObject (MetingImpl.class.getName());
               m.setDatum(new Date());
               m.setWaarde(5.0 + 0.1 * i);
               System.out.println("Meting " + m.datum() + " " + m.waarde() );
               so.addMeting(m);
            }
         }

            // close the connection
         db.close();

      }
      catch (Exception e)
      {
         System.out.println (e);
         e.printStackTrace();
         try
         {
            db.close();
         }
         catch (Exception ex)
         {
            System.out.println (ex);
         }
      }
   }
}

2.4.12. Weer5


import java.util.*;

import org.ozoneDB.DxLib.*;
import org.ozoneDB.*;
import be.khlim.weersmeting.*;

public class Weer5 extends Object
{
   public static void main (String[] args)
   {
      RemoteDatabase db = new RemoteDatabase();
        
      try
      {
         // open the connection on localhost at port 3333
         db.open ("localhost", 3333);
         System.out.println ("Connected ...");

         // reload our database classes if we changed them
         db.reloadClasses();

         WeersGegevens weer = (WeersGegevens)db.objectForName ("weer");
         if (weer == null)
         {
            System.out.println("weer niet gevonden");
            return;
         }

         Plaats pl = weer.plaatsForName("Diepenbeek");
         if (pl != null)
         {
            System.out.println ("Plaats " + pl.naam());

            // haal soort zonneintensiteit
            Soort so = pl.soortForName("zonneintensiteit");
            if (so != null)
            {
               System.out.println ("Soort " + so.naam());
            }
            else
            {
               System.out.println ("Soort niet gevonden");

               so = (Soort) db.createObject (SoortImpl.class.getName());
               so.setNaam ("zonneintensiteit");
               pl.addSoort(so);
            }

            // soort is gevonden
            // voeg Meting objecten bij
            DxBag lijst = so.alleMetingen();

            DxIterator it = lijst.iterator();
            while (it.next() != null)
            {
               Meting m = (Meting) it.object();
               System.out.println("Meting " + m.datum() + " " + m.waarde() );
            }
         }

            // close the connection
         db.close();

      }
      catch (Exception e)
      {
         System.out.println (e);
         e.printStackTrace();
         try
         {
            db.close();
         }
         catch (Exception ex)
         {
            System.out.println (ex);
         }
      }
   }
}

2.4.13. WeerBis


import java.io.*;

import javax.servlet.*;
import javax.servlet.http.*;

import org.ozoneDB.DxLib.*;
import org.ozoneDB.*;

import be.khlim.weersmeting.*;

/**
 * This is a simple example of an HTTP Servlet.  It responds to the GET
 * and HEAD methods of the HTTP protocol.
 */
public class WeerBis extends HttpServlet
{ 
    /**
     * Handle the GET and HEAD methods by building a simple web page.
     * HEAD is just like GET, except that the server returns only the
     * headers (including content length) not the body we write.
     */
    public void doGet (HttpServletRequest request,
                       HttpServletResponse response) 
        throws ServletException, IOException
        {
            PrintWriter out;
            String title = "Example Apache JServ Servlet";

            // set content type and other response header fields first
            response.setContentType("text/html");

            // then write the data of the response
            out = response.getWriter();
            
            out.println("<HTML><HEAD><TITLE>");
            out.println(title);
            out.println("</TITLE></HEAD><BODY bgcolor=\"#FFFFFF\">");
            out.println("<H1>" + title + "</H1>");
            out.println("Congratulations, ApacheJServ 1.1.2 is working!<br>");

            try
            {
               //OzoneProxy ozp = new WeersGegevensImpl_Proxy();
               out.println ("laadt proxy class be.khlim.weersmeting.WeersGegevensImpl_Proxy<br>");
               Class cl = Class.forName("be.khlim.weersmeting.WeersGegevensImpl_Proxy");
               out.println ("geladen " + cl.getName() + "<br>");
               cl.newInstance();
               out.println ("instance gemaakt<br>");
            }
            catch(Exception e)
            {
               out.println("<p>fout bij laden proxyclass" + e + "</p>");
            }


      RemoteDatabase db = new RemoteDatabase();

      try
      {
         // open the connection on localhost at port 3333
         db.open ("localhost",3333, "root","root");
         out.println ("Connected  to db " + db + "<br>");

         // reload our database classes if we changed them
         db.reloadClasses();
         out.println ("reloadClasses uitgevoerd<br>");

         WeersGegevens weer = (WeersGegevens)db.objectForName ("weer");
         if (weer == null)
         {
            out.println("<p>weer niet gevonden</p>");
         }
         else
         {
            out.println("<p>weer gevonden</p>");

            out.println("<ul>");

            Plaats pl;
            DxBag alles = weer.allePlaatsen();
            DxIterator it = alles.iterator();
            while (it.next() != null)
            {
               pl = (Plaats) it.object();
               out.println ("<li>Plaats " + pl.naam() + "<br>");


               out.println("<ul>");

               // haal soort zonneintensiteit
               Soort so = pl.soortForName("zonneintensiteit");
               if (so != null)
               {
                  System.out.println ("Soort " + so.naam());

                  DxBag lijst = so.alleMetingen();

                  DxIterator it2 = lijst.iterator();
                  while (it2.next() != null)
                  {
                     Meting m = (Meting) it2.object();
                     out.println("<li>");
                     out.println("Meting " + m.datum() + " " + m.waarde() );
                     out.println("</li>");
                  }

               }
               out.println("</ul>");
               out.println("</li>");
            }
            out.println("</ul>");

            // close the connection
            db.close();
         }
      }
      catch (Exception e)
      {
         out.println ("<p>" + e + "</p>");
         e.printStackTrace();

         out.println ("<pre>");
         e.printStackTrace(out);
         out.println ("</pre>");
         try
         {
            db.close();
         }
         catch (Exception ex)
         {
            out.println (ex);
         }
      }


            out.println("</BODY></HTML>");
            out.close();
        }
}

2.4.14. weer.xml


<?xml version="1.0"?>

<?cocoon-process type="xsp"?>

<?cocoon-process type="xslt"?>
<?xml-stylesheet href="weer-html.xsl" type="text/xsl"?>


<xsp:page
  language="java" 
  xmlns:xsp="http://www.apache.org/1999/XSP/Core">

 <xsp:structure>
   <xsp:include>org.ozoneDB.DxLib.*</xsp:include>
   <xsp:include>org.ozoneDB.*</xsp:include>
   <xsp:include>be.khlim.weersmeting.*</xsp:include>
 </xsp:structure>

 <xsp:logic>
  // klassevariabelen
  static private int counter = 0; 

  private synchronized int count()
  { 
    return counter++;
  }
 </xsp:logic>

 <weer>
    <xsp:logic>
      RemoteDatabase db = new RemoteDatabase();
      if (db == null)
      {
            <xsp:content>
              <melding>
                <xsp:attribute name="name">
                  <xsp:expr>db</xsp:expr>
                </xsp:attribute>

                <xsp:expr>db</xsp:expr>
              </melding>
            </xsp:content>
      }
      else
      try
      {
         // open the connection on localhost at port 3333
         db.open ("localhost", 3333, "root", "root");
         System.out.println ("Connected ...");

         // reload our database classes if we changed them
         db.reloadClasses();

         WeersGegevens weer = (WeersGegevens)db.objectForName ("weer");
         if (weer == null)
         {
            <xsp:content>
              <melding>
                <xsp:attribute name="name">
                  <xsp:expr>"weer niet gevonden"</xsp:expr>
                </xsp:attribute>

                <xsp:expr>db</xsp:expr>
              </melding>
            </xsp:content>
         }
         else
         {
              Plaats pl;
              DxBag alles = weer.allePlaatsen();
              DxIterator it = alles.iterator();
              while (it.next() != null)
              {
                 pl = (Plaats) it.object();
                 <xsp:content>
                 <plaats>
                   <xsp:attribute name="naam">
                     <xsp:expr>pl.naam()</xsp:expr>
                   </xsp:attribute>

                   <xsp:logic>
                   Soort so = pl.soortForName("zonneintensiteit");
                   if (so != null)
                   {
                      <xsp:content>
                      <soort>
                        <xsp:attribute name="naam">
                          <xsp:expr>so.naam()</xsp:expr>
                        </xsp:attribute>

                        <xsp:logic>
                        DxBag lijst = so.alleMetingen();
                        DxIterator it2 = lijst.iterator();
                        while (it2.next() != null)
                        {
                           Meting m = (Meting) it2.object();
                           <xsp:content>
                           <meting>
                             <xsp:attribute name="datum">
                               <xsp:expr>m.datum()</xsp:expr>
                             </xsp:attribute>
                             <xsp:attribute name="waarde">
                               <xsp:expr>m.waarde()</xsp:expr>
                             </xsp:attribute>
                           </meting>
                           </xsp:content>
                        }
                        </xsp:logic>
                      </soort>
                      </xsp:content>
                   }
                   </xsp:logic>
                 </plaats>
                 </xsp:content>
              }

            // close the connection
            db.close();
         }
      }
      catch (Exception e)
      {
            <xsp:content>
              <element>
                <xsp:attribute name="name">
                  <xsp:expr>db</xsp:expr>
                </xsp:attribute>

                <xsp:expr>e</xsp:expr>
              </element>
            </xsp:content>
         try
         {
            db.close();
         }
         catch (Exception ex)
         {
            System.out.println (ex);
         }
      }
    </xsp:logic>

 </weer>
</xsp:page>

2.4.15. weer-html.xsl


<?xml version="1.0"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
  <xsl:template match="weer">
    <xsl:processing-instruction name="cocoon-format">type="text/html"</xsl:processing-instruction>
    <html>
      <head>
        <title>Test weer</title>
      </head>
      <body>
        <ul>
          <xsl:apply-templates/>
        </ul>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="melding">
   <p>
    <xsl:apply-templates/>
   </p>
  </xsl:template>

  <xsl:template match="plaats">
    <li>
      <p>
        <xsl:value-of select="@naam"/>
      </p>
      <ul>
        <xsl:apply-templates/>
      </ul>
    </li>
  </xsl:template>

  <xsl:template match="soort">
    <li>
      <p>
        <xsl:value-of select="@naam"/>
      </p>
      <ul>
        <xsl:apply-templates/>
      </ul>
    </li>
  </xsl:template>

  <xsl:template match="meting">
    <li>
      <xsl:value-of select="@waarde"/>
      <xsl:value-of select="@datum"/>
    </li>
  </xsl:template>

</xsl:stylesheet>