Bob Swart (aka Dr.Bob)
Delphi 5 en Active Server Pages & Objects

Tijdens de Conference to the Max 2000 heb ik een sessie gedaan over Delphi en Active Service Objects. In dit artikel wil ik nog even terugkomen op enkele noemenswaardige bevindingen en ervaringen die ik de afgelopen tijd heb opgedaan met Active Server Pages en Delphi.

In een web browser kunnen we HTML pagina's bekijken. In deze pagina's kan van alles opgenomen zijn, van plaatjes tot informatie uit een database. Helaas zijn het vaak behoorlijk statische pagina's. We kunnen interactiviteit toevoegen door bijvoorbeeld gebruik te maken van Java of ActiveX controls (dat zullen we dus niet doen) of een scripting taal zoals JavaScript, VBScript of JScript. Dit zijn scripting talen die op de client pagina's werken, waarmee de HTML pagina dus van een stukje "processing" wordt voorzien. Aan de server kant is het wat moeilijker. Traditioneel zijn het CGI (common gateway interface) en ISAPI toepassingen die op de web server draaien en dynamische HTML pagina's uitspuwen. Nadeel is dat dit executables zijn, en als je wat wilt wijzigen moet je de gehele executable aanpassen. Als alternatief heeft Microsoft toen een server-side scripting taal bedacht - inderdaad: ASP oftewel Active Server Pages.

ASP
ASP versie 1.0 werd geïntroduceerd met NT 4 Service Pack 3 als onderdeel van Microsoft Internet Information Server (IIS) 3.0. Negen maanden later werd dit al vervangen door ASP 2.0, momenteel het meest gebruikt en beschikbaar in IIS 4.0 (onderdeel van de NT4 Option Pack). Op mijn laptop staat NT4 Server met SP5 en de NT4 Option Pack (overigens moet de de Service Patch 5 installeren ná de NT4 Option Pack, want er zitten toch nog wel wat sercurity lekken in ASP 2.0, en die worden door de Service Patches 4 en 5 grotendeels opgelost). Windows 2000 komt met IIS 5.0 en daarmee ook ASP 3.0 (die weer flink anders werkt, namelijk met MTS), maar ik moet eerlijk bekennen dat ik daar nog weinig ervaring mee heb. Delphi 5 bevat ondersteuning voor zowel ASP 2.0 als 3.0, zoals we straks zullen zien.

Active Server Pages
Een Active Server Page is een HTML pagina met de extentie .asp, die gewoon uit HTML bestaat, maar stukjes ASP script in zich heeft, tussen <% en %> tekens. Alles wat tussen <% en %> staat is script, en zal al door de web server geïnterpreteerd en uitgevoerd worden. In principe zijn er verschillende server-side scripting talen, maar de meeste voorbeelden in de literatuur gebruiken Microsoft's JScript of VBScript. In praktijk is de scripting taal echter niet het grootste probleem: dat wil je toch zoveel mogelijk minimaliseren, want geïnterpreteerde code is erg langzaam. Het voordeel van een server-side script is natuurlijk dat het makkelijk aan te passen en te onderhouden is, maar het moet niet (teveel) ten koste van de performance gaan (mijn andere sessie tijdens de CttM overigens). Mede daardoor is het binnen de ASP scripts mogelijk om gebruik te maken van stukken gecompileerde ASP code, ook wel Active Server Objects of ASP Components geheten. Deze Active Server Objects zijn in feite COM objecten als .ocx of .dll, die we vanuit een Active Server Page kunnen instantiëren en manipuleren. En vanuit een COM object is vele malen efficienter een query uit te voeren op een database tabel en het resultaat in HTML te produceren. Afgezien van het feit dat ik niet eens zou willen weten hoe ik dat in VBScript doe, want dit is een Delphi verhaal (toch?).

Delphi 5
Active Server Objects zijn in principe COM objecten die we in Delphi 3 en 4 zouden kunnen maken. Echter, Delphi 5 komt met een speciale Active Server Object Wizard die we natuurlijk even nader moeten bekijken (ik zal ook alleen maar Delphi 5 gebruiken in dit artikel, dus wie nog geen Delphi 5 gebruikt moet zich helaas even behelpen vrees ik). De ActiveX tab van de Object Repository bevat naast een icoontje voor een ActiveX library ook een icoontje voor de Active Server Object wizard. Natuurlijk moet de Active Server Object in een COM server (lees: ActiveX library) opgenomen worden, dus moeten we eerst de icon voor "ActiveX Library" kiezen, en dan pas het icoontje voor het "Active Server Object". Deze laatste stap levert de volgende dialoog, waarbij we even moeten opletten bij wat we invullen:

De CoClass Name is de interne naam van ons COM Object. We kunnen hier invullen wat we willen, zoals bijvoorbeeld "DOC". De Instancing en Threading Model velden hebben over het algemeen al de juiste default waarden. Later kunnen we hier van afwijken (door Single Instance te kiezen in plaats van Multiple Instance), maar voorlopig laat ik de opties gewoon zo staan. De Active Server Type opties zijn belangrijk. Afhankelijk van de versie van je web server (en daarmee de versie van ASP die ondersteund wordt) moet je hier voor de Page-level event methods (zoals OnStartPage en OnEndPage) kiezen, of voor de nieuwere ASP 3.0 Object Context. De meest voor de hand liggende keuze is de eerste - default - keuze voor ASP 2.0 die door IIS 3 en IIS4 ondersteund wordt. Alleen als je zeker weet dat je ASP 3.0 kan gebruiken - dus met IIS 5 of IIS 4 en MTS - moet je voor de Object Context kiezen. Ik heb nog geen praktijkervaring met deze laatste optie, maar heb begrepen dat het niet zo heel veel anders werkt. We komen hier op een later moment zeker nog op terug; voorlopig gebruiken we de meer toegankelijke Page-level event methods. Tot slot is de optie om een template script (lees: skelet .asp pagina) te laten genereren wel handig, zeker voor wie voor het eerst een ASP pagina en object aan het maken is.

Nadat we op de OK-knop hebben gedrukt wordt ons Active Server Object aangemaakt, inclusief een Type Library, en komen we zelfs als eerste meteen in de Type Library Editor terecht. Een Active Server Object is namelijk geen visuele form of iets anders, maar een non-visueel COM object, dat met de "buitenwereld" communiceert door zijn COM interface. En dat kunnen we definieren door de Type Library Editor te gebruiken; hier kunnen we nieuwe methoden (met parameters, etc) aanmaken en vervolgens in de Pascal code voor het ASP Object implementeren. Voordat we echter nieuwe methoden gaan maken, is het goed te zien dat we al een tweetal bestaande methoden hebben: de OnStartPage en OnEndPage methoden:

  unit DOC;
  interface
  uses
    ComObj, ActiveX, AspTlb, eBob42_TLB, StdVcl;

  type
    TDOC = class(TASPObject, IDOC)
    protected
      procedure OnEndPage; safecall;
      procedure OnStartPage(const AScriptingContext: IUnknown); safecall;
    end;

  implementation
  uses
    ComServ;

  procedure TDOC.OnEndPage;
  begin
    inherited OnEndPage;
  end;

  procedure TDOC.OnStartPage(const AScriptingContext: IUnknown);
  begin
    inherited OnStartPage(AScriptingContext);
  end;

  initialization
    TAutoObjectFactory.Create(ComServer, TDOC, Class_DOC,
      ciMultiInstance, tmApartment);
  end.
En nu kunnen we - in de Type Library Editor - een nieuwe methode toevoegen, zoals HelloWorld of iets anders. Argumenten zijn ook mogelijk, alhoewel die op dit moment nog weinig zinvols zouden kunnen doen. Zorg ervoor dat je ook op de "Refresh Implementation" knop drukt in de Type Library Editor, zodat een nieuw "skelet" voor de HelloWorld methode aanwezig is in onze DOC unit. In dit skelet kunnen we dan onze eigen ASP code gaan schrijven. Nu is het moment aangekomen dat we iets meer van ASP en de interactie van Active Server Pages en Objecten (componenten) moeten weten. Gelukkig is het niet zoveel anders als we met bijvoorbeeld WebBroker gewend waren: we hebben in principe een Request en een Response object. De Request bevat de eventuele invoer, terwijl de response de uitvoer bevat. In ons geval wil ik natuurlijk alleen maar even "Hello, world!" zeggen - of liever gezegd: Hello, World! in onze HTML pagina laten zetten. Daarvoor heb ik het Response object nodig, en wel de Write methode daarvan. Response.Write kunnen we gebruiken om HTML te produceren op de plaats waar in de Active Server Page de "aanroep" naar de betreffende methode staat.
  procedure TDOC.HelloWorld;
  begin
    Response.Write('<H1>Hello, world!</H1>');
    Response.Write('<HR>');
    Response.Write('The time is: '+TimeToStr(Now));
  end;
Dus, om bovenstaande HTML code te laten zien in onze ASP pagina, zal ik de aanroep moeten opnemen. Merk op dat ik dat moet doen na de (voor mij automatisch gegenereerde) regel waarin het Object gecreeerd wordt, anders werkt het natuurlijk niet.
  <% Set DelphiASPObj = Server.CreateObject("eBob42.DOC")
     DelphiASPObj.HelloWorld
  %>
Behalve de Write methode kunnen we nog veel meer met Response. Echter, in praktijk is Write de meest bruikbare en daardoor ook daadwerkelijk de meest gebruikte methode. Delphi's Code Insight (en on-line help) kan overigens veel meer details geven over de andere properties en methoden.

Activeren...
Ook Active Server Pages moeten op de juiste manier geaktiveerd kunnen worden. Allereerst moet de Active Server Page (het .asp document) in een directory staan op de web server waar de zgn. ASP scripting rights aan staan. Daarnaast moet het Active Server Object dat gebruikt wordt - aangenomen dat er eentje gebruikt wordt - aanwezig zijn en geregistreerd zijn op de web server. Active Server Objects kunnen op twee manier geregistreerd worden: als in-process of als out-of-process servers. Ik gebruik hier de in-process server type, die het meest voorkomt, en direct vanuit Delphi kan, namelijk door Run | Register ActiveX Server vanuit de Delphi IDE te doen. Indien nodig kunnen we altijd nog Run | Unregister ActiveX Server doen, uiteraard. Nadat de Active Server Page in de juiste directory staat en het Active Server Object op de web server geregistreerd is kan de Active Server Page worden opgeroepen in een web browser, die vervolgens de web server de opdracht zal geven op het script tussen de <% en %> uit te voeren, wat tot gevolg heeft dat een instantie van het Active Server Object "eBob42.DOC" gemaakt zal worden, en dat daarvan de methode HelloWorld aangeroepen zal worden. Met als resultaat een dynamische HTML pagina, die eerlijk gezegd nog weinig spannends bevat.

Delphi 5's ondersteuning wat betreft Active Server Pages is nog niet afgelopen. Een Active Server Object kan namelijk behalve de Request en Response ook nog van Session en Application objecten gebruik maken, en deze worden eveneens door Delphi 5 ondersteund. Wat echter veel leuker is, is het feit dat we ook reeds bestaande (ervaring met) Delphi 5 internet componenten, zoals de WebBroker en InternetExpress componenten, kunnen combineren met ASP. En dat is het onderwerp voor de rest van dit artikel.

ASO en Web Modules
Zoals we tot nu toe gezien hebben, zijn de Active Server Objects die we met Delphi kunnen maken met name beperkt in het genereren van dynamische HTML. De Response.Write method kent geen enkele optie om wat dat betreft ons leven gemakkelijker te maken. En dat terwijl er al vanaf Delphi 3 Client/Server een aantal HTML producerende componenten in Delphi zitten onder de naam Web Broker. Het moet toch niet moeilijk zijn om die te gebruiken binnen Active Server Objecten. Welnu, het is ook niet zo moeilijk. Het enige probleem is het feit dat een Active Server Object is niet visueel - zelfs niet tijdens design-time. Bij een web module heb je nog een speciaal soort data module waar je componenten op kan zetten, etc. maar bij een Active Server Object heb je alleen maar de klasse definitie en de Type Library, maar verder niks. En een simpele TPageProducer is nog wel dynamisch aan te maken, maar bij een TDataSetTableProducer kom je al snel in de problemen als je ook nog eens dynamisch de hele formatering, kleuren, etc. wil laten instellen. Dat kan eigenlijk alleen maar goed vanuit de Delphi IDE (de column property editor). Het is dus zaak om een data module of web module aan ons Active Server Object toe te voegen, zodat we daar de HTML producerende componenten op kunnen zetten en kunnen instellen naar wens. En tja, dan kom je al snel in potentiële multi-threading problemen: Een globale data module toevoegen aan een Active Server Object levert al snel problemen op als meerdere instanties toegang krijgen tot dit ene data module. Terwijl de ene query nog bezig is uitgevoerd te worden, wordt de nieuwe SQL tekst al opgebouwd. Om dat te voorkomen, kan ik ofwel ieder request (iederee client connectie) achter elkaar afhandelen - in een enkele thread - of moet ik zorgen dat ieder request zijn eigen instantie van de data module tot z'n beschikking heeft. De eerste oplossing kan werken in situaties waar niet veel mensen (tegelijkertijd) gebruik maken van het Active Server Object. De tweede oplossing is echter algemener, en groeit ook beter mee zonder dat mensen op elkaar hoeven te wachten.

TDOCDataModule in Object
De oplossing is dus om een data module (genaamd DataMod) binnen de scope van ons object toe te voegen, omdat instance data bij het apartment threading model veilig is. De klasse definitie van het Active Server Object wijzigt hierdoor een klein beetje, doordat we een private field DataMod van type TDOCDataModule toevoegen. Daarnaast moeten we dit data module natuurlijk wel op het juiste moment aanmaken en weer opruimen. De beste oplossing die ik hiervoor heb gevonden is om daar de OnStartPage en OnEndPage events voor te gebruiken die we bij ASP 2.0 er gratis bij krijgen. En uitgaande van de DOC Active Server Page krijgen we dan de volgende twee routines:

  procedure TDOC.OnStartPage(const AScriptingContext: IUnknown);
  begin
    inherited OnStartPage(AScriptingContext);
    DataMod := TDOCDataModule.Create(nil)
  end;

  procedure TDOC.OnEndPage;
  begin
    FreeAndNil(DataMod);
    inherited OnEndPage
  end;
En nu hoeven we alleen nog maar het data module van type TDOCDataModule te gebruiken als basis voor de WebBroker en InternetExpress component zoals de PageProducers en TableProducers. Stel bijvoorbeeld dat we een PageProducer in deze data module opnemen, en die willen gebruiken in onze voorgedefinieerde HelloWorld methode, dan kan dat als volgt:
  procedure TDOC.HelloWorld;
  begin
    Response.Write('<H1>Hello, world!</H1>');
    Response.Write('<HR>');
    Response.Write(PageProducer.Content);
  end;
De aanroep naar de WebBroker PageProducer.Content methode levert een (lange) string op, die we gewoon als argument van de ASP Response.Write method kunnen gebruiken. Een perfecte samensmelting tussen ASP om met de buitenwereld te communiceren en WebBroker componenten om de HTML te kunnen produceren. Er is zelfs nog meer mogelijk, maar dat komt een andere keer wel aan de orde.

Conclusie
Ik hoop een voorproefje te hebben gegeven van wat ASP is, wat je met ASP kan, en hoe ASP zich verhoudt tot bijvoorbeeld de WebBroker Technology (CGI en ISAPI) die we al kennen van Delphi. Persoonlijk vind ik vooral de geïntegreerde benadering erg sterk, door de WebBroker en InternetExpress componenten direct vanuit een Active Server Page Object te gebruiken.

Meer Informatie
Mocht iemand nog vragen, opmerkingen of suggesties hebben, dan hoor ik die het liefst via . Wie meer wil weten over web server toepassingen met Active Server Pages moet zeker eens overwegen om zich in te schrijven voor een van mijn Delphi Clinics.


Dit artikel is eerder verschenen in SDGN Magazine #61 - 2000

This webpage © 1999-2006 by webmaster drs. Robert E. Swart (aka - www.drbob42.com). All Rights Reserved.