Bob Swart (aka Dr.Bob)
Delphi 7 en IntraWeb 5

Let op: inmiddels is IntraWeb versie 5.1 ook beschikbaar!

Op vrijdag 13 december 2002 had ik het genoegen om tijdens de Conference to the Point een sessie te mogen houden over IntraWeb 5. Het goede nieuws is dat IntraWeb nu deel uitmaakt van Delphi 7, en iedereen die Delphi 7 al heeft aangeschaft kan dus gewoon "meespelen" met dit artikel. Wie Delphi 7 (nog) niet heeft kan natuurlijk ook een "losse" versie van IntraWeb aanschaffen, van de makers AtoZedSoftware zelf.

De IntraWeb die bij Delphi 7 zit, bestaat overgens uit twee edities. Allereerst is daar IntraWeb in "Page Mode" die bij Delphi 7 Professional zit, en met name bedoeld om bestaande WebBroker en/of WebSnap toepassingen uit te breiden met IntraWeb forms. Dat dit fijn is, daar kom je later in dit artikel wel achter. Dat het veel beter kan ook. Want naast IntraWeb 5 in Page Mode, bestaat er ook IntraWeb in Application Mode, en die zit bij Delphi 7 Enterprise. Gelukkig zit er ook een evaluatie versie van IntraWeb 5 Application Mode bij Delphi 7 Professional, dus zelfs als je geen Enterprise versie van Delphi 7 hebt kun je toch goed zien wat er met IntraWeb 5 in Application Mode mogelijk is.

IntraWeb Application Mode
Om een IntraWeb toepassing te bouwen doen we File | New - Other, en gaan we naar de IntraWeb tab van de Object Repository. Hier zien we een zevental icoontjes. De Page Form is speciaal voor IntraWeb in Page Mode, en zullen we vandaag niet gebruiken. De Application Form kan gebruikt worden om een nieuw form in Application Mode toe te voegen, en de overige vijf icoontjes kunnen gebruikt worden om een nieuw IntraWeb project te starten. De verschillende targets zijn Apache, Apache 2, ISAPI, Stand alone en Stand alone met data module (daar komen we straks nog op terug). Wat mij meteen opvalt is dat er geen CGI target is. Da's jammer, want één van mijn ISP's staat het me wel toe om CGI executables neer te zetten in de cgi-bin directory van mijn web server, maar geen ISAPI DLLs of andere dingen (zoals Apache of een stand-alone executable die zelf met de buitenwereld praat via een bepaalde poort). De reden is helaas eenvoudig: voor een CGI toepassing is het niet mogelijk om het state management te realiseren, dus is deze buiten de boot gevallen. Op zich wel te begrijpen, maar toch een dompertje voor wie (nog) niet de mogelijkheid heeft om ISAPI, Apache of een stand-alone toepassing te deployen op het internet.

Delphi 7 Object Repository - IntraWeb tab

Omdat een stand-alone application iets nieuws is (ten opzichte van bekende targets als Apache of ISAPI), stel ik voor dat we hiermee beginnen. Je mag ook meteen een "stand-alone application with data module" kiezen, maar ik kom pas later terug op de data module.
Na de keuze voor een IntraWeb project krijg je een beetje simpele dialoog van Delphi waarin je kan opgeven waar de IntraWeb project bestanden naartoe gekopieerd moeten worden. De dialoog is een beetje krot, want je kan er geen nieuwe directories in aanmaken (is waarschijnlijk nog steeds dezelfde dialoog die ook in Delphi 1 zat).

Select Directory dialoog

De reden dat we deze dialoog krijgen is dat de IntraWeb icoontjes allemaal corresponderen met een subdirectory in de C:\Program Files\Borland\Delphi7\Objrepos\IntraWeb directory. En een bijkomend voordeel van deze manier van werken is dat je nu zelf de bestanden in deze directories kan uitbreiden of aanpassen met code of extra componenten (kun je altijd nieuwe IntraWeb projecten of Page Forms toevoegen waar al wat opstaat). Kan handig zijn, maar ik moet toegeven dat ik het zelf nog maar een paar keer gedaan heb (tijdens het geven van demos).
Na het kiezen van de juiste directory in bovenstaande dialoog, krijg je een nieuw IntraWeb project met een tweetal units (of drietal als je ook voor de data module hebt gekozen): eentje voor de server controller en eentje voor het eerste IntraWeb form. Beide zijn nog leeg, maar zijn de moeite waard om even bij stil te staan voor we er in het wilde weg componenten op zetten.

TIWServerController
De Server Controller is een speciale module, waar iedere IntraWeb toepassing er maar eentje van nodig heeft. Hier worden de applicatie specifieke opties gezet. Voor een stand-alone toepassing betekent dit bijvoorbeeld dat we hier het poortnummer kunnen instellen waar de stand-alone toepassing naar gaat luisteren. Op een web server machine zal de "web server" software zelf al naar 80 luisteren, dus het is zaak om een ander - uniek - nummer te specificeren in de Port property (in onderstaande screenshot van de Object Inspector staat die nog op 0). Wie de evaluatie versie van IntraWeb 5 gebruikt zal er overigens niet in slagen om dit poortnummer te wijzigen - dat is een onderdeel van de "evaluatie", waarbij iedere keer dat de stand-alone toepassing start een nieuw random poortnummer gekozen zal worden. Goed geschikt om te testen, maar niet om echt te gebruiken. Bovendien zal de evaluatieversie alleen naar IP-adres 127.0.0.1 luisteren (de localhost), waar je bij de echte versie kan opgeven welk IP-address er gebruikt moet worden.
Een andere belangrijke property is de AppName property. Deze wordt gebruikt om IntraWeb stand-alone toepassingen van elkaar te onderscheiden, dus zeker als je meer dan één IntraWeb stand-alone toepassing wilt draaien is het zaak om de waarde MyIWApp te veranderen in iets anders - uniek voor iedere IntraWeb toepassing. De Description property is ter illustratie, en hoeft niet uniek te zijn. Overigens is het ook zaak om te zorgen dat iedere IntraWeb stand-alone toepassing (op dezelfde machine) via een andere poort te laten communiceren, maar dat spreekt vanzelf, neem ik aan.
Nog een handige property is de SupportedBrowser property, die default op [brIE,brNetscape6] staat, met de mogelijkheid om ook brOpera aan te zetten. Wie met een browser die niet aan "SupportedBrowsers" voldoet een IntraWeb toepassing wil bekijken krijgt meteen een foutmelding dat die browser niet ondersteund wordt! Om deze foutmelding te vermijden kun je ook brUnknown toevoegen voor "onbekende" browsers, maar dit is voor eigen riciso, want de gegenereerde HTML werkt niet in "onbekende" browsers, zoals Netscape 4 bijvoorbeeld. Als ouderwetse Netscape 4 gebruiker zit er voor mij dus niks anders op dan Internet Explorer te gebuiken om mijn IntraWeb 5 toepassingen te bekijken. Wie een IntraWeb web toepassing wil bouwen die ook met Netscape 4 te zien is kan dat beter met IntraWeb versie 4 doen. Deze oudere versie van IntraWeb ondersteunt wel Netscape 4, en wordt - op dit moment - nog steeds ondersteund door AtoZedSoftware. Voor hoelang is niet duidelijk, maar als er voldoende verzoeken binnenkomen on Netscape 4 te (blijven) ondersteunen, dan wordt het wellicht weer toegevoegd in een toekomstige versie of update.

TIWServerController

Login
Wie mijn WebSnap artikelen heeft gelezen weet dat WebSnap speciale voorzieningen heeft om login forms te maken, met de LoginFormAdapter, WebUserList, etc. IntraWeb doet dat iets eenvoudiger: via de AuthList TStrings property kun je een lijst met username=password opgeven. Dit leidt tot een pop-up dialoog (de standard authorisatie dialoog van de browser bij een HTTP authorization response). Nadeel is dat je deze pop-up dialoog niet kan uitbreiden met bijvoorbeeld de opmerking dat iemand ook als "gast" kan inloggen. Maar gelukkig is het niet moeilijk om zelf een loginscherm te bouwen. Dan kun je dus niet van de AuthList gebruik maken.
Wie niet een lijst met usernames en password wil opnemen, kan ook op de OnAuthRequest event handler reageren, en daarin code schrijven die controleert of de opgegeven username en password wel geldig zijn. Ook handig om een "backdoor" in te bouwen (bijvoorbeeld om te testen of voor onderhoud):

  procedure TIWServerController.IWServerControllerBaseAuthRequest(
    const AUserName, APassword: String; var AValid: Boolean);
  begin
    AValid := (AUserName = 'Zaphod') and (APassword = 'Beeblebrox')
  end;

Er wordt eerst gekeken of de opgegeven username en password voorkomen in de AuthList (als die al gevuld is), en pas daarna wordt de OnAuthRequest event handler gevuurd. Als geen van beide zijn ingevuld, is de IntraWeb toepassing voor iedereen bruikbaar en kun je eventueel je eigen custom login form toevoegen.

TformMain
Na de nodige administratie bij de Server Controller, is het nu tijd om te "spelen" met het lege IntraWeb form. Dit ziet er uit als een normaal Delphi form. En dat is een van de grootste voordelen van IntraWeb boven een framework als WebBroker of WebSnap: het biedt een echt RAD en bijna WYSIWYG interface tijdens design-time (niet helemaal WYSIWYG, want de data-aware componenten laten geen inhoud zien tijdens design-time, maar daar kan ik mee leven).
Voordat we naar de IntraWeb componenten gaan kijken, toch nog even opletten bij de form properties zelf, want niet alleen de TIWServerController heeft een SupporterBrowser property, ook alle IntraWeb forms hebben er een. En deze staat default op alleen [brIE,brNetscape6], dus als je hier niks aan doet kun je het IntraWeb form alleen bekijken met Internet Explorer of Netscape 6 (zelfs als je in de ServerController heb aangegeven dat bijvoorbeeld brOpera ook een optie is). Dit is iets wat ik altijd vergeet, dus even opletten.
Verder kun je natuurlijk ook de caption van het form zetten, alleen moet dat bij een IntraWeb form niet via de Caption (dat heeft geen effect), maar via de Title property. En ook de achtergrondkleur of het plaatje (via een filename or URL) en zelfs stylesheet kun je hier opgeven in de betreffende properties.
Wat ons dan eindelijk aanbrengt bij de IntraWeb componenten zelf. Op het eerste gezicht net "normale" Delphi componenten, verdeeld over een viertal tabs: IW Standard, IW Data, IW Client Side en IW Control.

IW Standard
De IW Standard tab bevat 24 componenten, namelijk: TIWApplet, TIWButton, TIWCheckBox, TIWComboBox, TIWEdit, TIWFile, TIWFlash, TIWHRule, TIWImage, TIWImageFile, TIWList, TIWLabel, TIWListbox, TIWLink, TIWMemo, TIWMenu, TIWRadioGroup, TIWRectangle, TIWRegion, TIWText, TIWTimer, TIWGrid, TIWTreeview, en TIWURL.

De meeste hiervan zullen voor zich spreken, maar enkele zijn nieuw of bijzonder. Zo is er belangrijk verschil tussen de TIWImage en TIWImageFile. De eerste bevat zelf een image, en genereert iedere keer weer een JPG plaatje dat van de server naar de browser wordt gestuurd. De tweede daarentegen bevat slechts de verwijzing naar een externe image, via ofwel een filename of een URL, en is daarmee efficienter in gebruik. De TIWImage wordt vooral gebruikt om zelf plaatjes te genereren (bijvoorbeeld een grafiekje). Het verschil tussen TIWLink en TIWURL is dat de laatste springt naar een URL, terwijl de eerste een OnClick event handler heeft waarvoor we zelf code kunnen schrijven (voor de eindgebruiker is er nauwelijks verschil te zien, maar het gedrag is anders).

IW Data
De IW Data tab bevat 13 data-aware componenten, namelijk: TIWDBCheckBox, TIWDBComboBox, TIWDBEdit, TIWDBGrid, TIWDBImage, TIWDBLabel, TIWDBListbox, TIWDBLookupListbox, TIWDBLookupCombobox, TIWDBFile, TIWDBMemo, TIWDBNavigator, en TIWDBText.

Tijdens design-time laten deze data-aware componenten niet de inhoud van de dataset zien (zoals "normale" Delphi data-aware componenten), wat met name bij het TIWDBGrid een beetje verwarrend kan werken. Tijdens run-time wordt er gewoon HTML geproduceerd, dus ziet alles er netjes uit. De TIWDBNavigator is bijzonder flexibel omdat we hier eigen plaatjes in kunnen hangen, eigen hintteksten kunnen opgeven en achter iedere button een eigen event handler kunnen hangen. De TIWDBGrid daarentegen lijkt wat beperkter, met name omdat deze geen mogelijkheden biedt om de data in het grid te editen (tenzij ik iets over het hoofd zie). Voor een edit grid gebruik ik nu een third-party IntraWeb component uit de IntraWeb Component Pack Pro van TMS Software - zie http://www.tmssoftware.com voor meer details.

IW Client Side
De IW Client Side tab bevat vijf speciale componenten, namelijk: TIWCSLabel, TIWCSNavigator, TIWDynamicChart, TIWDynamicChartLegend en TIWDynGrid. Deze componenten zijn te gebruiken als client-side componenten, waarbij er dus niet steeds met de web server gecommuniceerd hoeft te worden, maar er lokaal genavigeerd kan worden, en lokaal grafieken en grids vertoond kunnen worden.

Daar horen dan nog wel enkele ondersteunende componenten bij die de lokale data bevatten, en die vinden we - samen met nog enkele laatste componenten - terug op de IW Control tab.

IW Control
Op de IW Control tab vinden we acht componenten, namelijk: TIWTemplateProcessorHTML, TIWLayoutMgrForm, TIWPageProducer, TIWModuleController, TIWClientSideDataset, TIWClientSideDatasetDBLink, TIWStandaloneServer, en TIWLayoutMgrHTML.

De twee ClientSideDataSets zijn bedoeld om lokaal (client-side dus) data te laden, die dan door de componenten van de IW Client Side tab gebruikt kunnen worden. Dit scheelt behoorlijk in de performance. De overige componenten kunnen gebruikt worden als ondersteuning bij het bouwen van templates, het bepalen van de layout of voor de Page Mode koppeling met WebBroker en WebSnap toepassingen - daar heb ik het een andere keer nog wel over.
Het daadwerkelijk gebruiken van componenten om een mooi IntraWeb form te bouwen laat ik aan de fantasie van de lezer over; het zal duidelijk zijn dat er op deze manier met IntraWeb bijzonder fraaie web toepassingen gebouwd kunnen worden, in een record tijd.

Meerdere Forms
Een IntraWeb toepassing bestaat al snel uit meerdere forms. En daar komt iets bijzonders bij kijken. Bij een "normale" Delphi toepassing is het gebruikelijk om een tweede form te creëren, en dan met ShowModal te vertonen, en tot slot weer met Free op te ruimen, iets als volgt:

  with TTweedeForm.Create(Application) do
  try
    ShowModal
  finally
    Free
  end;

Met IntraWeb gaat dat een beetje anders. Ten eerste kennen we geen ShowModal meer, ten tweede bestaat er ook geen Free meer. In plaats van ShowModal moeten we gewoon Show aanroepen. Maar het effect is ongeveer hetzelfde, want op het web worden nieuwe forms letterlijk bovenop de oude forms vertoond. We zien dus altijd maar één IntraWeb form - de bovenste van de stapel. En doordat de Show non-blocking is, kan het vorige form de volgende niet opruimen, dus is het aan het bovenste form zelf om zichzelf op te ruimen "na gebruik". Dat kan door bijvoorbeeld een TIWButton of TIWLink component te gebruiken, en dan in de OnClick event handler de Release methode aan te roepen. Hierdoor wordt het nieuwe form gesloten (en het geheugen weer vrijgegeven), en komt het vorige form weer in beeld. Release kun je net zovaak aanroepen totdat er geen forms meer zijn, en dan is de IntraWeb toepassing afgelopen. Natuurlijk kan de gebruiker ook eerder al het browser window sluiten, maar dat is niet het einde van de sessie (de IntraWeb toepassing op de server zal niet te horen krijgen dat het browser window gesloten is, en zal het sessie object voor het browser window nog in de lucht houden tot deze een timeout krijgt. De waarde van timeout is default ingesteld op 10 minuten, wat je natuurlijk bij de TIWServerController kan instellen met de SessionTimeout property - zie de de screenshot van de Object Inspector nog maar een keer).
Een manier die niet werkt om van een nieuw form terug te gaan naar het vorige form is de back button. Deze wordt expliciet uitgezet door IntraWeb. En zelfs als dat uitzetten niet lukt (bijvoorbeeld door de HistoryEnabled property van de TIWServerController op True te zetten), dan nog is het gebruik van de back button niet aan te raden. Je komt dan namelijk terug in "een vorige toestand", en zou zomaar dingen kunnen aanpassen die toch al niet meer geldig zijn. Zoals de reclame over autodrop al aangaf: het zou verboden moeten worden. Gelukkig is het ook verboden, want als je in een vorige form op die manier iets probeert te wijzigen zul je een waarschuwing van IntraWeb ontvangen, en terugkeren naar je huidige situatie.

State Management
En dit vormt een mooie overgang naar het onderwerp state management binnen IntraWeb. Volgens de documentatie is het bijhouden van state informatie volledig "transparant", maar er wordt vervolgens niet verteld hoe dat dan transparant werkt. Wie even een kijkje neemt achter de schermen, ziet al snel hoe eenvoudig het werkt. In de unit van de TIWServerController is een speciale class genaamd de TUserSession. Bij een nieuwe IntraWeb toepassing (zonder data module) is deze class nog leeg, maar we kunnen zelf zaken toevoegen aan de TUserSession, zoals bijvoorbeeld een veld genaamd "Info" van type Integer, als volgt:

  TUserSession = class(TComponent)
    Info: Integer;
  end;

We kunnen nu vanuit ieder IntraWeb form gebruik maken van onze eigen user session, via de aanroep naar UserSession , waar we dan het veld Info van kunnen lezen en schrijven. Gewoon UserSession.Info dus. Het lijkt nu op het eerste gezicht dat we een globale variabele UserSession gebruiken. In praktijk blijkt echter dat UserSession een functie is die gebruik maakt van het Data veld van de WebApplication die gekoppeld is aan onze eigen instantie. Er kan dus ook niemand anders aan onze UserSession komen, en we kunnen er instoppen wat we willen. De WebApplication, en daarmee het Data veld dat de UserSession bevat, wordt pas vrijgegeven als het laatste IntraWeb form afgesloten is met Release, of als er een timeout optreeedt (of als een van de Terminate methodes van de WebApplication wordt aangeroepen, maar die had ik nog niet eerder genoemd).

Data Modules
Wie in het begin al meteen een IntraWeb stand-alone toepassing met data module had gemaakt zal gezien hebben dat de TUserSession helemaal niet leeg was. Integendeel, er zat een veld DataModule1 in alsmede een constructor Create. En niet zonder reden, want het gebruik van een data module is natuurlijk iets dat niet zomaar kan, omdat een IntraWeb toepassing door meerdere requests tegelijkertijd in gebruik kan zijn. Met als gevolg dat een data module wel moet weten bij wie hij hoort. Of nog beter: dat een data module alleen toegankelijk is binnen één bepaalde request/thread, en wat is dan een betere plek dan de TUserSession class? Dat is nu precies wat er gebeurt als je een IntraWeb toepassing met data module wilt hebben. De constructor zorgt ervoor dat er bij het aanmaken van de TUserSession ook een instantie van de data module wordt aangemaakt. En via UserSession.DataModule1 kunnen we overal vandaan bij de data module komen. Inderdaad behoorlijk transparant. Zeker als je ziet dat je ook direct aan "DataModule1" kunt refereren (zonder UserSession ervoor), omdat de data module unit al een functie bevat die DataModule1 heet en de juiste data module (uit onze user session) teruggeeft. In code is het duidelijk wat er gebeurt:

  function DataModule1: TDataModule1;
  begin
    Result := TUserSession(RWebApplication.Data).Datamodule1
  end;

Als je meer dan één data modules nodig hebt dien je die uiteraard zelf op vergelijkbare wijze toe te voegen aan de TUserSession class.
IntraWeb state management werkt overigens alleen in Application Mode, en dus niet in Page Mode.

Tot slot...
Nog enkele tips voor wie met IntraWeb 5 aan de slag wil. Het is erg verleidelijk om meteen alle mooie componenten te gaan gebruiken, en achter ieder handig event een stukje code voor de event handler te schrijven (niet alleen de OnClick, maar ook de OnChange enzo). Dat leidt onder Windows namelijk ook tot fijne interactieve toepassingen die lekker werken. Echter, bij een web toepassing zijn de "regels" een beetje anders: iedere event handler betekent "server side" code, en heeft tot gevolg dat er vanuit de browser even een request naar IntraWeb toepassing op de server wordt gestuurd (met de state informatie erbij), en het antwoord weer terug in de browser komt. Dan is op een lokale machine nog lekker snel, maar op het internet kan dat al gauw leiden tot vertragingen of zelfs een volledig onbruikbare toepassing die bij elke klik weer een paar seconden bezig is met het uitvoeren van een server request. Gebruik event handlers dus met mate.
Een ander iets om even op te letten bij IntraWeb is de hoeveelheid HTML die some geproduceerd wordt. Omdat ieder IntraWeb component op de Render message reageert door een stukje HTML (en soms ook JavaScript) te genereren, en dat allemaal bijelkaar wordt samengevoegd tot de uiteindelijke pagina, kan het makkelijk voorkomen dat er niet echt efficiënte HTML code in de uiteindelijke pagina staat, wat wellicht minder (en dus kleiner en sneller) zou kunnen. Een vergelijkbare toepassing met WebSnap kan in die gevallen kleinere - en snellere - pagina's opleveren. Het is geen ramp, maar ook iets om in de gaten te houden als (bandbreedte) performance een issue gaat worden.
Los van deze twee kleine (potentiële) minpuntjes, en het ontbreken van expliciete ondersteuning voor Netscape 4 browsers kan ik alleen maar enthousiast zijn over IntraWeb.

Meer Informatie
Mocht iemand nog vragen, opmerkingen of suggesties hebben, dan hoor ik die het liefst via . Wie meer wil weten over IntraWeb kan bij de website van AtoZedSofware terecht, en zou zeker eens een bezoek aan mijn Delphi en IntraWeb Clinic moeten overwegen (ik ben een IntraWeb Authorized Trainer).


Dit artikel is eerder verschenen in SDGN Magazine #76 - februari 2003

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