Delphi 5 en CORBA Exceptions |
We kennen natuurlijk allemaal de standaard try-except en try-finally exceptions in Delphi. Maar Delphi is niet de enige taal die exceptions kent: ook Java en C++ kennen exceptions, alhoewel die daar met throw-catch om je oren vliegen. In CORBA's IDL (interface definition language) spreekt men weer van het "raisen" van exceptions, dus wat dat betreft lijkt het wat meer op de ObjectPascal variant die wij als Delphi gebruikers al kennen. Het IDL exception type is in feite een struct (een record in Delphi) dat verschillende data members kan bevatten, maar geen methoden (in tegenstelling tot Delphi exceptions, waaraan je wel methoden kunt toevoegen). In IDL hebben exceptions dus wel een toestand (de data members) maar geen gedrag. Dat is op zich ook niet zo onlogisch, want een CORBA exception wordt meestal door de CORBA server gegenereerd (met raise) en dan als foutboodschap naar de CORBA client gestuurd. Het heeft weinig zin om specifiek gedrag (methodes) toe te voegen aan de exception, als dit gedrag toch vaak geen betekenis heeft voor de client. En ik vermoed dat het toevoegen van callback methods aan exceptions gewoon een beetje teveel van het goede werd - zeker als je bedenkt dat IDL als onderdeel van CORBA door het OMG committee gestandardiseerd is.
Als we even terugdenken aan het IDL voorbeeld dat we de afgelopen nummers steeds als rode draad hebben gebruikt, dan hadden we in module DrBob42 een interface ISDGN met als methode TheAnswer. Deze method has als out argument een Answer van type long. Het kan echter voorkomen dat het antwoord nog niet bekend is (als men nog aan het rekenen is), of dat er iets anders misgaat. Dus laten we een exception NoAnswerYet definiëren dat aan zou moeten geven dat er nog geen antwoord bekend is (en dat men ook niet net als meneer van Dalen zou willen wachten op dat antwoord, want dat kan wel eens erg lang gaan duren). De IDL definitie van een exception NoAnswerYet is als volgt:
exception NoAnswerYet { };Merk op dat het woord exception hier (syntactisch gezien) als een soort reserved word wordt gebruikt: een soort struct definitie inderdaad. Om ook nog een data member "Reason" toe te voegen moeten we de IDL definitie als volgt aanpassen:
exception NoAnswerYet { string Reason; };De CORBA Server kan de Reason een waarde geven, en de exception NoAnswerYet raisen als het antwoord nog niet bekend is. Hoe dat (dan de server kant) gaat zal ik zo laten zien. Eerst zullen we de IDL moeten aanpassen met de exception definitie zelf. Bovendien moet in CORBA IDL - net als in andere talen maar helaas niet in Delphi - bij een method worden aangegeven welke exceptions er geraised kunnen worden in deze methode. Voor onze "TheAnswer" methode is dat de exception "NoAnswerYet", en dat ziet er alles bij elkaar als volgt uit:
module DrBob42 { exception NoAnswerYet { string Reason; }; interface ISDGN { void TheAnswer(out long Answer) raises (NoAnswerYet); }; interface SDGNFactory { ISDGN CreateInstance(in string InstanceName); } };Overigens had de exception definitie ook binnen het interface ISDGN gedefinieerd kunnen worden. In dat geval kan niemand anders deze exception raisen, en dat wordt dan ook vaak gebruikt voor exceptions die slechts lokale betekenis hebben (iets wat hier eigenlijk ook geldt).
Standaard Exceptions
Er bestaan ook al een verzameling voorgedefinieerde exceptions in IDL, die we ook in Delphi's ObjectPascal terugvinden (mits de VisiBroker for Delphi 5 is geïnstalleerd).
Ze zijn afgeleid van type SystemException, en staan in CORBA.PAS.
Een lijstje van exception type name met betekenis is als volgt:
type SystemException = class(Exception) UNKNOWN De "unknown" exception BAD_PARAM Een "invalid" parameter is doorgegeven NO_MEMORY Geheugenallocatie faalde IMP_LIMIT Overschreden implementatie limiet COMM_FAILURE Communicatie fout INV_OBJREF Invalide object referentie NO_PERMISSION Geen permissie voor deze methode INTERNAL Interne ORB fout MARSHAL Marshal fout bij parameter of resultaat INITIALIZE ORB initialisatie fout NO_IMPLEMENT Implementatie van methode niet beschikbaar BAD_TYPECODE Foute TypeCode BAD_OPERATION Foute operatie (methode) NO_RESOURCES Onvoldoende resource beschikbaar voor request NO_RESPONSE Geen antwoord PERSIST_STORE Persistente opslag mislukt BAD_INV_ORDER Verkeerde aanroepvolgorde routines TRANSIENT Transient failure FREE_MEM Kan geheugen niet vrijgeven INV_IDENT Foute identifier syntax INV_FLAG Foute vlag meegegeven INTF_REPOS Fout bij benadering Interface Repository BAD_CONTEXT Fout bij afhandeling Context Object OBJ_ADAPTER Fout gevonden bij Object Adaptor DATA_CONVERSION Data conversie fout OBJECT_NOT_EXIST Objet bestaat niet (meer) - verwijderenTot slot kennen we ook nog de UserException, een eigen type binnen CORBA, waar onze "NoAnswerYet" exception van afgeleid zal worden:
UserException = class(Exception)
Exceptions Raisen
Omdat we zelfs met VisiBroker for Delphi geen CORBA servers op basis van IDL kunnen maken, zijn we gedwongen om de nieuwe CORBA server in bijvoorbeeld C++Builder of Java te maken.
Ik heb hier voor C++Builder 4 Enterprise gekozen, en heb via IDL-2-CPP een Server Skeleton laten genereren voor C++Builder.
De method ISDGNImpl::TheAnswer heb ik als volgt geïmplementeerd in C++Builder:
void ISDGNImpl::TheAnswer(CORBA::Long Answer) { throw DrBob42::NoAnswerYet("De Server is nog niet klaar met rekenen..."); //return 42; }Dit natuurlijk alleen maar om de demonstreren dat we een exception aan de CORBA server kant kunnen genereren, die we vervolgens aan de CORBA client kant in Delphi kunnen opvangen en afhandelen.
Exception Afhandelen
Met de IDL2PAS kunnen we de nieuwe interface definities in DrBob42.IDL vertalen naar een nieuwe DrBob42_C.pas en DrBob42_I.pas.
De DrBob42_C.pas bevat de Client Stub, waaronder de definitie (en implementatie) van de NoAnwerYet exception.
Dit is te zien in onderstaand fragment:
ENoAnswerYet = class(UserException) private FReason: AnsiString; protected function _get_Reason: AnsiString; virtual; public property Reason: AnsiString read _get_Reason; procedure Copy(const _Input : InputStream); override; end;Wat opvalt is dat de exception NoAnswerYet een E voor de naam heeft gekregen in Delphi (terwijl we in C++Builder toch echt een exception van type NoAnswerYet konden raisen). Verder zien we de property Reason, en de rest is eigenlijk voor ons gebruik niet van belang. De code om de exception af te vangen is zoals we dat gewend zijn in Delphi met een normaal try-except blok:
try Server.TheAnswer(Answer); except on E: ENoAnswerYet do ShowMessage(E.Reason); end;
Conclusie
VisiBroker for Delphi 5 biedt ondersteuning voor CORBA exceptions.
Helaas nog niet voor CORBA servers geschreven in Delphi - deze maken nog gebruik van de Type Library die geen CORBA exceptions ondersteunt - maar wel voor CORBA clients geschreven in Delphi die communiceren met CORBA servers geschreven in een willkeurige andere taal die CORBA exceptions ondersteunt (zoals Java of C++Builder).
Het wachten is nu op Kylix - de Delphi/C++Builder voor Linux die naar verwachting wel een volledig functionele VisiBroker implementatie zal bevatten, waaronder die voor Server Skeletons met ondersteuning voor exceptions.
Ik verwacht dat die VisiBroker for Delphi dan ook nog voor Delphi 5 Enterprise beschikbaar komt, of anders uiterlijk in Delphi 6 Enterprise aanwezig zal zijn.
Tegen die tijd zullen we nogmaals het ontwerp CORBA aansnijden, dat spreekt haast vanzelf...
Mocht iemand nog vragen, opmerkingen of suggesties hebben, dan hoor ik die het liefst via .