MAGAZINE. www.sdn.nl IN DIT NUMMER O.A.:



Vergelijkbare documenten
Full text search met Lucene op Azure

MyDHL+ Van Non-Corporate naar Corporate

SAMPLE 11 = + 11 = + + Exploring Combinations of Ten + + = = + + = + = = + = = 11. Step Up. Step Ahead

2019 SUNEXCHANGE USER GUIDE LAST UPDATED

General info on using shopping carts with Ingenico epayments

MyDHL+ ProView activeren in MyDHL+

ANGSTSTOORNISSEN EN HYPOCHONDRIE: DIAGNOSTIEK EN BEHANDELING (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM

!!!! Wild!Peacock!Omslagdoek!! Vertaling!door!Eerlijke!Wol.!! Het!garen!voor!dit!patroon!is!te!verkrijgen!op! Benodigdheden:!!

150 ECG-problemen (Dutch Edition)

Introductie in flowcharts

Hoe met Windows 8 te verbinden met NDI Remote Office (NDIRO) How to connect With Windows 8 to NDI Remote Office (NDIRO

Activant Prophet 21. Prophet 21 Version 12.0 Upgrade Information

Duurzaam projectmanagement - De nieuwe realiteit van de projectmanager (Dutch Edition)

RECEPTEERKUNDE: PRODUCTZORG EN BEREIDING VAN GENEESMIDDELEN (DUTCH EDITION) FROM BOHN STAFLEU VAN LOGHUM

(1) De hoofdfunctie van ons gezelschap is het aanbieden van onderwijs. (2) Ons gezelschap is er om kunsteducatie te verbeteren

Settings for the C100BRS4 MAC Address Spoofing with cable Internet.

Firewall van de Speedtouch 789wl volledig uitschakelen?

De grondbeginselen der Nederlandsche spelling / Regeling der spelling voor het woordenboek der Nederlandsche taal (Dutch Edition)

It s all about the money Group work

Preschool Kindergarten

B1 Woordkennis: Spelling

Borstkanker: Stichting tegen Kanker (Dutch Edition)

Group work to study a new subject.

Comics FILE 4 COMICS BK 2

Read this story in English. My personal story

Het beheren van mijn Tungsten Network Portal account NL 1 Manage my Tungsten Network Portal account EN 14

L.Net s88sd16-n aansluitingen en programmering.

ALGORITMIEK: answers exercise class 7

S e v e n P h o t o s f o r O A S E. K r i j n d e K o n i n g

Handleiding Zuludesk Parent

Ontpopping. ORGACOM Thuis in het Museum

Shipment Centre EU Quick Print Client handleiding [NL]

EM7680 Firmware Update by OTA

Appendix A: List of variables with corresponding questionnaire items (in English) used in chapter 2

Puzzle. Fais ft. Afrojack Niveau 3a Song 6 Lesson A Worksheet. a Lees de omschrijvingen. Zet de Engelse woorden in de puzzel.

Hoe te verbinden met NDI Remote Office (NDIRO): Apple OS X How to connect to NDI Remote Office (NDIRO): Apple OS X

ETS 4.1 Beveiliging & ETS app concept

Registratie- en activeringsproces voor de Factuurstatus Service NL 1 Registration and activation process for the Invoice Status Service EN 10

Add the standing fingers to get the tens and multiply the closed fingers to get the units.

LONDEN MET 21 GEVARIEERDE STADSWANDELINGEN 480 PAGINAS WAARDEVOLE INFORMATIE RUIM 300 FOTOS KAARTEN EN PLATTEGRONDEN

Taco Schallenberg Acorel

Vertaling Engels Gedicht / songteksten

A2 Workshops Grammatica Heden

voltooid tegenwoordige tijd

Intermax backup exclusion files

L.Net s88sd16-n aansluitingen en programmering.

Een vrouw, een kind en azijn (Dutch Edition)

EM6250 Firmware update V030507

Zo werkt het in de apotheek (Basiswerk AG) (Dutch Edition)

Stars FILE 7 STARS BK 2

TFS als perfecte tool voor Scrum

Duiding Strafuitvoering (Larcier Duiding) (Dutch Edition) Click here if your download doesn"t start automatically

Bijlage 2: Informatie met betrekking tot goede praktijkvoorbeelden in Londen, het Verenigd Koninkrijk en Queensland

ICARUS Illumina E653BK on Windows 8 (upgraded) how to install USB drivers

CTI SUITE TSP DETAILS

Handleiding Installatie ADS

Archief Voor Kerkelijke Geschiedenis, Inzonderheid Van Nederland, Volume 8... (Romanian Edition)

MyDHL+ Uw accountnummer(s) delen


Engels op Niveau A2 Workshops Woordkennis 1

Een vrouw, een kind en azijn (Dutch Edition)

Cambridge Assessment International Education Cambridge International General Certificate of Secondary Education. Published

FOR DUTCH STUDENTS! ENGLISH VERSION NEXT PAGE. Toets Inleiding Kansrekening 1 8 februari 2010

Duurzaam projectmanagement - De nieuwe realiteit van de projectmanager (Dutch Edition)

Grammatica uitleg voor de toets van Hoofdstuk 1

FOD VOLKSGEZONDHEID, VEILIGHEID VAN DE VOEDSELKETEN EN LEEFMILIEU 25/2/2016. Biocide CLOSED CIRCUIT

Registratie- en activeringsproces voor de Factuurstatus Service NL 1 Registration and activation process for the Invoice Status Service EN 11

How to install and use dictionaries on the ICARUS Illumina HD (E652BK)

Usage guidelines. About Google Book Search

Over dit boek. Richtlijnen voor gebruik

Onder de motorkap van Microsoft Azure Web Sites. Eelco Koster Software architect ORDINA

Buy Me FILE 5 BUY ME BK 2

Luister alsjeblieft naar een opname als je de vragen beantwoordt of speel de stukken zelf!

Yes/No (if not you pay an additional EUR 75 fee to be a member in 2020

Vergaderen in het Engels

Continuous Delivery. Sander Aernouts

Functioneel Ontwerp / Wireframes:

OPEN TRAINING. Onderhandelingen met leveranciers voor aankopers. Zeker stellen dat je goed voorbereid aan de onderhandelingstafel komt.

FAAC DRIVER. Driver install procedure for FAAC boards. Installatieprocedure voor driver voor FAAC-kaarten.

Main language Dit is de basiswoordenschat. Deze woorden moeten de leerlingen zowel passief als actief kennen.

AlarmShield Interactive Security System Quickstart Guide. Model No. OPG2204

Davide's Crown Caps Forum

Find Neighbor Polygons in a Layer

Mobile Devices, Applications and Data

MyDHL+ Exportzending aanmaken

Procedure Reset tv-toestellen:

Travel Survey Questionnaires

Ius Commune Training Programme Amsterdam Masterclass 15 June 2018

Help je Power BI Analytics project om zeep 6 succesfactoren. Marc Wijnberg Gebruikersdag 2018

Win a meet and greet with Adam Young from the band Owl City!

The first line of the input contains an integer $t \in \mathbb{n}$. This is followed by $t$ lines of text. This text consists of:

Handleiding Digipass DP310

Maillijsten voor medewerkers van de Universiteit van Amsterdam

My Inspiration I got my inspiration from a lamp that I already had made 2 years ago. The lamp is the you can see on the right.

Tim Akkerman - Head of Mobile

3 I always love to do the shopping. A Yes I do! B No! I hate supermarkets. C Sometimes. When my mother lets me buy chocolate.

01/ M-Way. cables

BISL EEN FRAMEWORK VOOR BUSINESS INFORMATIEMANAGEMENT (DUTCH LANGUAGE) (GERMAN EDITION) (DUTCH EDITION) BY REMKO VAN DER POLS, RALPH DONA

MyDHL+ Duties Taxes Paid

Transcriptie:

MAGAZINE SOFTWARE DEVELOPMENT NETWORK ISSN: 2211-6486 IN DIT NUMMER O.A.: Full text search met Lucene op Azure < Async/await en het restaurantmodel < Talking about Frames < Continuous Deployment met TFS, Octopusdeploy en Azure < Application Insights for Visual Studio Online < Nummer 121 juni 2014 SDN Magazine verschijnt elk kwartaal en is een uitgave van Software Development Network 121 www.sdn.nl

Colofon Uitgave: Software Development Network Tweeëntwintigste jaargang No. 121 juni 2014 Bestuur van SDN: Marcel Meijer, voorzitter Rob Suurland, penningmeester Remi Caron, secretaris Redactie: Marcel Meijer (redactie@sdn.nl) Aan dit magazine werd meegewerkt door: Roel Hans Bethlehem, Bob Swart, Maarten van Stam, Arjen Bos, Alexander Meijers, Remi Caron, Marcel Meijer en natuurlijk alle auteurs! Listings: Zie de website www.sdn.nl voor eventuele source files uit deze uitgave. Contact: Software Development Network Postbus 506, 7100 AM Winterswijk Tel. (085) 21 01 310 Fax (085) 21 01 311 E-mail: info@sdn.nl Vormgeving en opmaak: Reclamebureau Bij Dageraad, Winterswijk www.bijdageraad.nl 2014 Alle rechten voorbehouden. Niets uit deze uitgave mag worden overgenomen op welke wijze dan ook zonder voorafgaande schriftelijke toestemming van SDN. Tenzij anders vermeld zijn artikelen op persoonlijke titel geschreven en verwoorden zij dus niet noodzakelijkerwijs de mening van het bestuur en/of de redactie. Alle in dit magazine genoemde handelsmerken zijn het eigendom van hun respectievelijke eigenaren. Adverteerders Microsoft 2 Achmea 40 Beste SDN magazine lezer, voorwoord De Olympische spelen zijn inmiddels afgelopen, maar de Oranje gekte gaat de volgende fase in. Want de wereldkampioenschappen Voetbal beginnen zeer binnenkort in Brazilië. De tenniswedstrijden op Roland Garos en Wimbledon zijn voor ons Nederlanders in het verleden ook mooie toernooien geweest. Maar deze trekken naast het wereldkampioenschap Hockey in Den Haag, de Giro in Italië en de Tour de France toch minder bekijks en spreken minder tot de verbeelding dan het Voetbal. Als je het zo bekijkt is het een aaneenschakeling van sportevenementen voor elk wat wils. Onze ICT wereld is dat eigenlijk ook. Is de ene release geweest, dan staat de volgende op het punt het levenslicht te zien. Hebben we net de Build Conference gehad met de verschillende aankondigen, worden er in Teched Noord Amerika weer nieuwe aankondigingen gedaan. En als je dan denkt dat het even rustig is, dan komen Apple of Google of Delphi met hun Conferenties en hun nieuwtjes. Kortom ook dit is een aaneenschakeling van aankondigingen en nieuwe producten. Zeker nu we bijna allemaal gebruik maken van een Agile manier van werken, volgen de vernieuwingen elkaar nog sneller op. Hierdoor gaat de wereld sneller dan enige jaren geleden. Voor een jong vakgebied is dat best knap. Om bij te blijven en op de hoogte te blijven van de nieuwste ontwikkelingen proberen wij je met ons magazine volledig up-to-date te houden. Ook dit magazine staat weer boordevol met nuttig en bruikbare informatie. Onze auteurs hebben allemaal stuk voor stuk hun uiterste best gedaan om mooie, aansprekende en informerende artikelen te maken. Ik vind dat ze daarin zijn geslaagd. Zo hebben we artikelen over Async programmeren, Dynamic Frames met Delphi, het nieuwe Application Insights van Visual Studio Online, Column over Agile van Sander, Hoe nog beter te kunnen samenwerken door een stukje toegevoegde techniek, coolere website met HTML5, Column van Michiel over de Wolkenarchitect, Remote applications on Microsoft Azure, hoe Lucene te gebruiken op Azure, Delphi XE5, de combinatie van Continous deployment met TFS en Octopusdeploy en wat kleine nieuwtjes over het Microsoft Azure platform. Kortom een lekker dik nummer met mooie artikelen. Graag horen we welke artikelen jullie zelf zouden willen schrijven. Er zit een auteur in ons allemaal. Veel leesplezier en mail ons jullie vragen cq. opmerkingen. Groeten, Marcel Eindredacteur Magazine en Voorzitter SDN Adverteren? Informatie over adverteren en de advertentietarieven kunt u vinden op www.sdn.nl onder de rubriek Magazine. magazine voor software development 3

Agenda 2014 Inhoud 6 juni 2014 SDN event 2 22 augustus 2014 SDN magazine 122 8-9 september 2014 SDC/SDE+ 27-31 oktober 2014 Teched (Barcelona) 14 november 2014 SDN magazine 123 medio december 2014 SDN event 3 03 Voorwoord Marcel Meijer 04 Inhoudsopgave 04 Agenda 05 Full text search met Lucene op Azure Roy de Boer en Freek Paans 10 Working software over proce$$es and fools Sander Hoogendoorn 13 Delphi REST Client Components Bob Swart 16 Async/await en het restaurantmodel Piet Amersfoort 20 Talking about Frames Cary Jensen 24 Applicatiestack opnieuw uitvinden met HTML5 Niels Bergsma 26 Continuous Deployment met TFS, Octopusdeploy en Azure Eric Tummers 31 Wolkenarchitect Michiel van Otegem 32 Reach further by developing collaboratively Isak Edblad 34 Application Insights for Visual Studio Online Hassan Fadili 38 Microsoft Azure Remote Apps Marcel Meijer

CLOUD Roy de Boer en Freek Paans Full text search met Lucene op Azure Lucene is een full text search engine, oorspronkelijk geschreven in Java. Het biedt mogelijkheden om grote hoeveelheden tekst efficiënt te doorzoeken. In dit artikel beschrijven we onze ervaringen met het inzetten van Lucene.NET (een populaire.net port van Lucene) binnen een Azure-omgeving, de cloud-oplossing van Microsoft. Azure stelt namelijk een aantal specifieke eisen aan de architectuur van een applicatie, die ook van invloed zijn op de Lucene implementatie. We stellen daartoe een dergelijke architectuur voor en onderzoeken een aantal karakteristieken hiervan. Bij dit artikel hoort een voorbeeldproject, waarin we de publieke domein boeken van gutenberg.org doorzoekbaar maken. In dit voorbeeldproject passen we de voorgestelde architectuur toe. Daarnaast kan dit project ook als basis dienen om zelf verder met Lucene op Azure te experimenteren. Het project is te vinden op https://github.com/infi-nl/lucene-azure. We zullen nu beginnen met een korte review van de Lucene API, waarna we ingaan op de Azure-specifieke uitdagingen. Vervolgens tonen we een mogelijke implementatie en sluiten af met een analyse van de operationele karakteristieken van die implementatie. Lucene Vroeger was het gebruikelijk om informatie te zoeken aan de hand van sleutelwoorden in een kaartenbak. Dit was beter dan niets, maar met de komst van zoekmachines is het gebruikelijk geworden om informatie te zoeken op basis van willekeurige tekstfragmenten. Dit concept staat bekend als full text search. Mocht je zelf full text search willen aanbieden om applicatie-specifieke informatie doorzoekbaar te maken, dan bieden veel databases daar standaard mogelijkheden voor. Als die mogelijkheid er echter niet is, zoals in Azure SQL Database, dan bestaan er ook externe componenten die dit kunnen, zoals Lucene. Dit is een populaire keuze voor Java-projecten, en wordt onder andere gebruikt door Solr en ElasticSearch. Daarnaast is het project inmiddels ook naar.net geport, deze port gebruiken we voor dit artikel. Lucene Document Waar binnen een relationele database een record de kleinste eenheid van opslag is, is dat binnen Lucene een Document. Dit is een datastructuur waarmee je de te doorzoeken data in één of meerdere velden kunt onderbrengen. Naast deze zelf te definiëren velden, houdt Lucene ook een intern ID bij voor elk document. Dit ID zullen we later terugzien bij het ophalen van resultaten. Zo maken we in het voorbeeldproject per boek een document aan, met daarin de volgende velden: Gutenberg ID, titel, publicatiedatum op Gutenberg, taal, auteur en de volledige tekst van het boek. Dit maakt het mogelijk om bijvoorbeeld specifiek op Gutenberg ID of taal te zoeken. IndexReader en IndexWriter Lucene slaat alle documenten op in de zogenoemde index. Net als bij bijvoorbeeld een relationele database zorgt de index ervoor dat er efficiënt gezocht kan worden. Het uitlezen van en wegschrijven naar deze index wordt respectievelijk door de classes IndexReader en IndexWriter verzorgd. Deze classes zijn ontworpen met multithreaded applicaties in het achterhoofd: een enkele instantie kan worden gedeeld tussen alle threads binnen de applicatie zonder dat je zelf voor synchronisatie hoeft te zorgen. In verband met performance en geheugengebruik wordt geadviseerd om met één instantie per applicatie te werken. In ieder geval kan de fysieke index maar door één IndexWriter tegelijkertijd worden bewerkt. In het algemeen is de geïndexeerde informatie, en zo ook de Lucene index, aan verandering onderhevig. IndexReader detecteert wijzigingen niet automatisch. De wijzigingen worden pas zichtbaar na het aanmaken van een nieuwe instantie. Hoe we hier mee omgaan, laten we verderop in het artikel ook zien. Query en IndexSearcher Zoekopdrachten binnen Lucene worden gëencapsuleerd in een Query object. Een dergelijk object is op meerdere manieren te verkrijgen, bijvoorbeeld via de QueryParser class die een tekstuele query zoals titel:max AND auteur: mult* vertaalt naar een Query. Maar er is bijvoorbeeld ook de TermQuery. De Query kan vervolgens door de Search methode van de class IndexSearcher worden uitgevoerd. Dit levert een op relevantie gesorteerde lijst met Lucene Document ID s op. IndexSearcher is net als IndexReader en IndexWriter threadsafe, waardoor één instantie per applicatie voldoende is. Directory Toegang tot de fysieke index wordt geregeld door implementaties van de abstract class Directory, zoals bijvoorbeeld FSDirectory of RAMDirectory (die de index respectievelijk op het filesystem of in RAM opslaan). Tot zover de review van de Lucene API. We gaan nu verder in op de Azure-specifieke uitdagingen. magazine voor software development 5

CLOUD Uitdagingen op Azure Om Lucene te gebruiken op Azure moet je rekening houden met een aantal uitgangspunten van Azure: In een Azure-omgeving moet je elk logisch onderdeel van je systeem op minimaal twee nodes draaien om aanspraak te kunnen maken op de SLA van 99.95% beschikbaarheid. Zo zul je minimaal twee nodes van je web- of worker-roles nodig hebben. Binnen de Azure-rollen heb je te maken met een transient harddisk. Dat wil zeggen dat bestanden die je wegschrijft op het lokale filesystem niet durable zijn; als Azure de node re-imaged krijg je weer een kaal filesystem. Aangezien Azure dit op willekeurige tijden doet, moet je hier rekening mee houden bij het ontwikkelen van je applicatie. Voor het gebruik van Lucene heeft dit een aantal implicaties. Een request als GET /Search?q=foo+bar komt namelijk op een willekeurige web-node uit. De node in kwestie zal toegang moeten hebben tot de fysieke index om het request af te kunnen handelen. In een dergelijke multi-node situatie zijn er twee voor de hand liggende opties voor de locatie van de index: ofwel het lokale filesystem, ofwel een shared storage, in het geval van Azure bijvoorbeeld de Windows Azure Blob Storage. De oplossing met het lokale filesystem is vrij complex omdat je zelf de index op de diverse nodes gesynchroniseerd moet houden. Je loopt dan al snel tegen situaties aan waar je gebruik moet maken van gedistribueerde transacties om de indices consistent te houden. Een ander probleem is het initialiseren van een kale node, bijvoorbeeld omdat een node wordt toegevoegd, of een bestaande node opnieuw wordt ge-imaged. In dergelijke situaties moet je de index herbouwen, wat afhankelijk van de grootte een tijdrovende klus kan zijn. We hebben daarom gekozen voor de oplossing waar we de index delen via een shared storage. Hierbij zullen de indices wel altijd in-sync zijn. Er kleeft echter ook een nadeel aan: elke node moet updates kunnen doorvoeren op de index, terwijl er maar één actieve Index Writer kan zijn. In principe regelt Lucene deze synchronisatie zelf via locking, maar nodes moeten hierdoor op elkaar wachten. Hierdoor kunnen de responsetijden oplopen en kunnen er uiteindelijk zelfs time-outs optreden. Dit probleem is op te lossen door één proces aan te wijzen dat alle writes doet. Andere nodes geven dan hun gewenste wijzigingen door aan dit proces. In onze implementatie noemen we dit proces de Indexer, en communiceren we de updates via een queue. Nadeel van deze oplossing is wel dat je eventual consistency introduceert: na een write zal het even duren voordat deze zichtbaar is op elke node. De tijd tussen het doorgeven van een wijziging en het daadwerkelijk zichtbaar worden op een web-node noemen we de update-latency. Een ander probleem is dat de index niet op het lokale filesystem leeft en de daarom benodigde netwerktoegang tot de shared storage extra latency zal opleveren. Dit probleem zou je kunnen verzachten door de index lokaal te cachen. Lucene leent zich hier goed voor omdat updates incrementeel te downloaden zijn. Beide oplossingen voor de genoemde problemen worden geïmplementeerd door de vrij beschikbare component AzureDirectory (http://azuredirectory.codeplex.com). Dit is een implementatie van de Lucene Directory die de index opslaat op de Blob Storage. Bovendien wordt de index ook in een cache op het lokale filesystem bijgehouden. Een complete oplossing zou er dan zo uit kunnen zien: In dit schema valt op dat er twee Indexer nodes zijn (waar het Indexer proces op draait), dit is nodig vanwege de Azure SLA voorwaarden. We leggen later uit hoe we zorgen dat er slechts één actief is. We zullen nu de geschetste architectuur verder uitwerken en onderzoeken. Implementatie Er zijn twee aspecten waar een wezenlijk verschil ontstaat tussen deze architectuur en een single-node situatie: het doorvoeren van updates en het verversen van de IndexReader. Omdat ons artikel specifiek gaat over Lucene op Azure zullen we deze aspecten verder uitdiepen. Index updates Een update op de index bestaat uit twee stappen: allereerst wordt de gewenste update gequeued door een web-node, waarna hij wordt gedequeued en aan de index wordt toegevoegd door het Indexer proces. Het queuen gebeurt, in pseudocode, als volgt: Book book = ; Queue queue = ; queue.enqueue(serialize(book)); Het verwerken van de update ziet er dan zo uit: IndexWriter writer = ; Queue queue = ; Message message = queue.dequeue(); Book book = Deserialize(message.Body); writer.adddocument(tolucenedocument(book)); writer.commit(); message.complete(); De call naar Message.Complete wordt gedaan omdat er gewerkt wordt met een peek-lock pattern. Dit voorkomt verlies van berichten bij fouten tijdens de verwerking. Het is dan wel belangrijk om Message.Complete pas na writer.commit aan te roepen. Azure Service Bus Queues biedt deze peek-lock semantiek en gebruiken we dan ook in onze implementatie. De API s van Lucene en de Service Bus bieden beide mogelijkheden tot batchverwerking. Wanneer er meerdere berichten beschikbaar zijn 6 MAGAZINE

CLOUD in de queue kunnen deze in één keer worden verwerkt. Zoals we straks laten zien heeft dit invloed op de performance. De batch size noemen we B. Dat ziet er dan zo uit: IndexWriter writer = ; Queue queue = ; int batchsize = ; Message[] messages = queue.receivebatch(batchsize); foreach (var message in messages) { AddMessageToIndex(writer, message); writer.commit(); queue.completebatch(messages); Bij het bepalen van de batch size kijken we naar de verwerkingssnelheid van de Indexer en naar de update-latency : function GetIndexWriter() { Directory directory = Ö; while (true) { try { return new IndexWriter (directory); catch (LockObtainFailedException) { Sleep(Ö); De LockObtainFailedException treedt op wanneer een Indexer-node al een IndexWriter open heeft op de index. Met deze constructie kan er dus altijd maar één Indexer-node berichten verwerken. Verversen van de IndexReader Voor het lezen van de index implementeren we een IndexReader singleton: class IndexReaderSingleton { static _indexreader = new IndexReader(..) static GetInstance() { return _indexreader; We zien dat een batch size van minder dan B=200 berichten de verwerkingssnelheid beperkt. Voor batch sizes groter dan 200 neemt de verwerkingssnelheid niet meer toe. De optimale verwerkingssnelheid is dus te bereiken door de batch size op minimaal B=200 berichten te stellen. Als er updates plaatsvinden op de index worden deze pas zichtbaar als we een nieuwe IndexReader openen. We hebben voor een strategie gekozen waarbij de IndexReader periodiek, dus elke x seconden, opnieuw geopend wordt. Dit zorgt er dus voor dat we elke x seconden een verse IndexReader hebben. We noemen die periode de verversperiode R. Het verversen gaat dan als volgt: class IndexReaderSingleton { static Refresh() { _indexreader = new IndexReader( ); Schedule.Every(x seconden, IndexReaderSingleton.Refresh) We hebben voor de overzichtelijkheid synchronisatie- en lockingoverwegingen buiten beschouwing gelaten. We hebben ook gekeken hoe de update-latency zich bij verschillende aanvoersnelheden v (updates per seconde) gedraagt: We zien dat er voor batch sizes groter dan 175 berichten geen significant verschil in update-latency is bij deze aanvoersnelheden. We zien ook dat rond de 100 updates per seconde verzadiging van de queue plaatsvindt en dat de latencies daardoor oplopen. Dit strookt met de observaties met betrekking tot de verwerkingssnelheid. Een laatste overweging bij implementatie is de Azure SLA: het proces dat schrijft zal ook dubbel uitgevoerd moeten worden, maar er mag slechts één IndexWriter tegelijkertijd actief zijn. Dit lossen we op door het synchronisatiemechanisme van Lucene te gebruiken: Een nadeel van deze oplossing is dat we steeds een compleet nieuwe IndexReader openen, wat potentieel duur is. We benutten daarom de Reopen()-methode van de IndexReader, deze methode vult de door de huidige IndexReader geladen data aan met nieuw beschikbare informatie. class IndexReaderSingleton { static Refresh() { _indexreader = _indexreader.reopen(); Voor het uitvoeren van zoekopdrachten hebben we tot slot de Index- Searcher nodig. Deze heeft een afhankelijkheid op de IndexReader en daarom laten we de lifecycle van de IndexSearcher samenlopen met die van de IndexReader. magazine voor software development 7

CLOUD class IndexSearcherSingleton { static _indexreader = new IndexReader(Ö) static indexsearcher = new IndexSearcher(_indexReader); static Refresh() { IndexReader newreader = _indexreader.reopen(); if (newreader == _indexreader) { return; _indexreader = newreader; indexsearcher = new IndexSearcher(_indexReader) static GetInstance() { return _indexsearcher; We maken hierbij gebruik van het feit dat _indexreader.reopen zichzelf teruggeeft als de index onveranderd is. Een zoekopdracht neemt dan de volgende vorm aan: IndexSearcher indexsearcher = IndexSearcherSingleton.GetInstance(); string userquery = ; Query query = new QueryParser( ).parse(userquery); TopDocs topdocs = indexsearcher.search(query, ); TopDocs bevat dan de lijst met de meest relevante Lucene Document ID s. Dan moeten we nog een keuze maken voor de waarde van de verversperiode R. Om dit te bepalen bekijken we hoe de updatelatency hierdoor beïnvloed wordt bij verschillende aanvoersnelheden v. We zien dat de aanvoersnelheid in de gevallen v=0, v=10, v=20 updates per seconde geen significante invloed heeft op de updatelatency bij verschillende verversperiodes. Voor een aanvoersnelheid van v=50 updates per seconde zie je een hogere latency voor verversperiodes kleiner dan 2000 ms, dan voor 2500 ms. We hebben dit verder niet onderzocht. Daarnaast zien we dat de update-latency in de overige gevallen lineair schaalt met de verversperiode. Een tweede overweging is de interactie met de Blob Storage. We hebben daarom gemeten hoeveel data er vanaf de Blob Storage gedownload wordt per web-node, uitgezet tegen de tijd. Tijdens deze meting was de aanvoersnelheid 75 updates per seconde: We zien dat de verversperiode geen significante invloed heeft op de hoeveelheid gedownloade data. Overigens zijn de duidelijke stappen in de grafiek toe te schrijven aan het index merge proces van Lucene. Dit proces optimaliseert de fysieke index door onder andere data samen te voegen. Deze samengevoegde data staat dan niet in de cache, en moet dus in zijn geheel gedownload worden. De initiële download is 465 MB, wat ruwweg overeenkomt met de grootte van de index op de Blob Storage, 425 MB. Er blijkt dus dat een verversperiode van 2500 ms voor alle onderzochte aanvoersnelheden resulteert in een update-latency van lager dan 4 seconden. Daarbij levert dit ten opzichte van langere verversperiodes geen groot verschil op in traffic naar de Blob Storage. We hebben echter het gebruik van systeemresources niet gemeten en kunnen ons voorstellen dat er problemen kunnen ontstaan bij korte verversperiodes, vooral wanneer je het opruimen van de IndexReader en Index- Searcher overlaat aan de garbage collector. Dit lichten we hieronder verder toe. Dispose versus GC In bovenstaande implementatie laten we het opruimen van de oude IndexReader en IndexSearcher over aan de garbage collector.dit kan ervoor zorgen dat sommige resources (zoals files) onnodig lang geopend blijven. Als dit een probleem blijkt kun je ervoor kiezen om de Dispose()-methodes van IndexSearcher en IndexReader aan te roepen na gebruik. Pas echter op: meerdere threads kunnen een reference hebben naar deze objecten en Dispose() mag pas aangeroepen worden nadat alle threads hebben aangegeven klaar te zijn met hun reference. Een methode om dit te doen is het gebruik van reference counting. Een mogelijke implementatie hiervan tonen we in het voorbeeldproject. Operationele karakteristieken Als laatste zijn we geïnteresseerd in de operationele eigenschappen van deze oplossing. We kijken daarvoor naar enkele omgevingsparameters: wat is de invloed van het aantal gebruikers op de zoeksnelheid, en wat is de invloed van het aantal documenten op de update-latency. 8 MAGAZINE

CLOUD We hebben gemeten hoe de zoekperformance schaalt met het aantal zoekende gebruikers (gesimuleerd met meerdere threads) bij twee verschillende indexgroottes N (aantal documenten in de index). We doen dit door te zoeken op willekeurige zoektermen en de 10 meest relevante resultaten op te vragen: We zien dat een concurrency tot 25 threads per node in beide gevallen een zoeklatency van minder dan 100 ms oplevert. Bij 100.000 documenten is er nog ruimte voor hogere concurrency bij gelijke zoeklatency. Voor 1.000.000 documenten is dit echter niet het geval; daar loopt de zoeklatency snel op. Het aantal concurrent zoekopdrachten dat je per node kunt bedienen wordt dus beperkt door de indexgrootte. Tenslotte onderzoeken we hoe de update-latency zich gedraagt bij verschillende indexgroottes. Hiertoe meten we de update-latency als functie van het aantal documenten in de index N, bij verschillende aanvoersnelheden v. Dit hebben we verder niet onderzocht. De updatedoorvoersnelheid van 100 updates per seconde is in onze gevallen toereikend geweest. We hebben daarom niet onderzocht of we deze nog hoger konden krijgen. De Indexer lijkt wel moeilijker uit te schalen omdat er maar één Indexer actief kan zijn. Deze eigenschappen maken dat wij Lucene een geschikte oplossing vinden voor het aanbieden van full text search in een Azure-omgeving. In de praktijk zetten wij de geschetste oplossing met succes in binnen meerdere projecten. We zijn hierbij niet tegen grote verrassingen aangelopen en zullen Lucene blijven overwegen voor nieuwe projecten. Roy de Boer Roy de Boer (roy@infi.nl) is één van de oprichters van Infi. Naast een voorliefde voor C#, en de oneindige tocht naar elegant softwareontwerp, is hij ook nogal enthousiast over OS X en FreeBSD. Freek Paans Het blijkt dat het aantal documenten in de index geen significante invloed heeft op de update-latency. Daarnaast is de gemeten ondergrens van de update-latency 2.0 s. Conclusie We hebben de Lucene API in het kort geïntroduceerd en de problemen om deze op Azure in te zetten uiteengezet. Vervolgens hebben we een architectuur die deze problemen oplost voorgesteld en de karakteristieken hiervan doorgemeten. We hebben de volgende observaties gedaan voor een implementatie die gebruik maakt van Azure Service Bus voor queueing en Azure Blob Storage voor persistent storage voor de index: De zoeklatency is bij indices tot 1.000.000 documenten en een concurrency van 25 threads minder dan 100 ms. De indexgrootte heeft een duidelijke invloed op de zoeklatency: grote indices hebben in het algemeen een hogere latency en beperken bovendien de concurrency per node ten opzicht van kleinere indices. De indexgrootte heeft geen significante invloed op de updatelatency. We kunnen tot 100 updates per seconde op de index doorvoeren, hiervoor is wel een batch size van minimaal 200 berichten vereist. Bij een verversperiode van 2.5 s zien we een update-latency van 4 seconden. De traffic van de Blob Storage wordt niet significant beïnvloed door de verversperiode. Lucene op Azure is daarmee binnen onze projecten een schaalbare oplossing gebleken. Binnen een node vinden wij de performance van Lucene acceptabel en je ziet bovendien ook lineair gedrag bij het bijschakelen van web-nodes. Uiteindelijk zal de Blob Storage waar de index op staat mogelijk een bottleneck worden. Freek Paans (freek@infi.nl) werkt als technisch directeur bij Infi. Hij houdt zich voornamelijk bezig met het optimaliseren van het softwareontwikkeltraject binnen Infi, maar typt ook nog actief mee aan verschillende projecten. Verder vindt hij het leuk om na te denken over vrijwel elk vraagstuk in de softwareontwikkeling: van low-level performancewerk tot teamorganisatie tot conceptontwikkeling. Freek is ook te vinden op twitter onder @FreekPaans. 1 We gebruiken in de metingen documenten tussen de 0 en 3 kb. Mocht je meer vragen hebben over de meetmethode dan kun je deze per mail aan ons stellen. TIP: Downloaden SDN Magazines Op de site van SDN vind je niet alleen een archief van alle magazines, het is ook mogelijk om de magazines te downloaden: www.sdn.nl/magazine. magazine voor software development 9

GENERAL Sander Hoogendoorn Working software over proce$$es and fools Quite frequently people in agile (and non-agile) projects or at conferences and workshops come up to me and ask me what tooling they should use. Which online agile dashboard tooling do you recommend us?, What is the best code repository? or even We are doing a Scrum project. Are we allowed to use a UML modeling tools such as Enterprise Architect in Scrum? In all situations, I reply using the all-time favorite Scrum Master - which I m not and never will be - response: it depends. Not because I don t know much about agile tooling, but because there s many different sides to the subject. To be honest, in my opinion, the discussion around agile tooling is expanding faster than the universe. Agile hippies and Scrumdamentalists First of all, there are groups in the agile community - if there ever was a single one - with highly idealistic attitudes, where people have this almost John Lennon-esque view of how agile will make the world a better place and will bring world peace at last. This is a world where all tools are considered evil, and are comparable to releasing drones in Afghanistan. A world where using tools feels cheating. These agile hippies as I refer to them rely heavily on the left side of the Agile Manifesto. This is where Working software over comprehensive documentation turns into No, we do not document in agile projects, and where Individuals and interaction over processes and tools become No, you re not allowed to use a modeling tool in an agile project. To me, although this is an interesting view, it is also a rather naïve way of looking at the world of agile. Next, there are what I refer to as the Scrumdamentalists. With the rising popularity of agile and agile approaches such as Scrum and now Agility Path - basically Scrum but with the word enterprise added to the names of all the roles and deliverables - many newbies have enrolled in Scrum Mastery; well-willing people with brand new certificates but hardly any real-life experience in agile projects. Scrumdamentalists will follow the Scrum Guide, which they consider to be their bible, quite literally. Now the Scrum Guide is an interesting read as it comes to tooling, as it hardly discusses tooling. The Scrum Guide does not refer to unit testing frameworks, or how to set up your dashboard, or even where to store your user stories. In fact, the Scrum Guide does not even mention user stories. At all. The agile suits On the other, total opposite side of the spectrum, we find the vendors. Agile is more and more becoming a vehicle to make money. We already had a plethora of monopolized certification programs - or scams if you wish - to make money from, but more recently we are seeing that agile principles and techniques are penetrating board rooms of large enterprises. Hence the sudden need from agile methodologists to quickly come up with enterprise agile approaches. Think Disciplined Agile Delivery, SAFe and Agility Path - which, with the risk of being called blasphemous, you could describe as the Scrum Guide with the word enterprise taped to it. And when there s enterprises involved, there s tools to be sold, and there s money to be made. So the vendors, which I refer to as the agile suits, enter the agile arena as well. Some vendors are achieving this by displaying agile street credibility, such as Atlassian, who print t- shirts for nerds -yes, which I do wear and send funny videos on how to pair program out on YouTube. 10 MAGAZINE

GENERAL As you can imagine this strategy goes down quite well with the agile hippies. Other vendors such as IBM Rational, have long established board room credibility, and have found ways to sell their often quite expensive and extensive tool sets to CTO s and CIO s. Tools that are often not specifically targeted at agile, but have been lying around on shelves anyway. A company I know just bought around a million dollars worth of tooling on a large vendor s application lifecycle management platform to run their enterprise agile projects. Same ball game, different strategies. Fuzzy But let s be honest, besides these somewhat black-and-white views on the world, it is virtually impossible or at least undesirable to run an agile software development project without tools. If you are trying to organize your rehousing using agile techniques - which I did once -just using post-its on the kitchen door can be enough tooling, but not when you re delivering working software. For instance, there seems to be little debate about using a development environment. Think Visual Studio, Eclipse, IntelliJ. And although I ve seen passionate discussion between developers about which development environment to use, nobody will dispute that you need one. But after that, it gets fuzzier. When discussing agile tooling there is a wide variety of categories to consider. Are you discussing Scrum Boards, Kanban Boards, agile dashboards or Task Boards for instance? Most co-located teams use post-its on the wall, but distributed teams are going to have to rely on something more electronically organized boards. But then what? Mingle? Trello? Speedbird9? Jira Agile? If you re a two man co-located team, post-its on the wall will do fine, and you can easily produce your burn-down chart on a piece of paper or an Excel spread sheet. If you are an international trading company that develops new software around the clock with a highly distributed team of for example requirements people in Singapore and New York, analysts in Stockholm, developers in Poland, and testers in Germany, as I ve seen recently, you might best consider using a complete application lifecycle management platform. At last, some guidelines I do feel sorry if you hoped that this article would present you with the out-of-the-box right tool choice for your project. I am unable to make such recommendation. Your project can and is very likely very different from mine. It might have a different number of teams, a different number of people, different technology, and a different geographical distribution of people. There is no one-size-fits-all agile tool set, as there is no one-size-fits-all agile process. However, I do want to leave you with some practical guidelines when it comes to choosing a specific tool set for your project. So here they are: Start minimal. Always start with the minimal set of tools that will do the work for your current situation. It is always easier to add tools later, then to have to remove tools which you ve been using for half a year just because they didn t add much value. 3M. If you are a co-located team that is not re-building the entire application landscape of a large insurance company, my favorite 3M tooling will do the job nicely: start with post-its on the wall. On my current project we have set up a special room with projector and comfy seats just for doing demo s and retrospectives. Oh, and for doing modeling sessions, because we are using a modeling tool for use cases and domain modeling Enterprise Architect in this particular case. So can you use modeling tools in agile projects? Well, I think you can. If you really need it. I wouldn t recommend setting up a modeling environment such as Enterprise Architect for a six week mobile project, but if you intend to rebuild all administrative systems of let s say an insurance company, a modeling environment is indispensable. And what about bug tracking? We do need tools for that, right? Should we use Basecamp? Or should we use Jira instead? Recently I visited a project where the tester explained that whenever he found a bug on a feature he was testing, he created an issue in Jira. The developer then could pick it up from Jira, and work on it. Sounds reasonable right? It s just that in this project both the tester and the developer are always working on the same feature, and they are literately sitting back-to-back in the office. And what about bug tracking? Management reporting? Tools for unit testing? Integration testing? Continuous integration? Continuous delivery? Are you getting on the continuous delivery train? There are even so many categories of tools, that it s almost impossible to name them all. If you had enough time, you could probably create a short list for each of the categories, but then again, every project if different, so the requirements for tools in each project is likely different too. YAGNI This is where, as often, the YAGNI acronym comes in handy. You Ain t Gonna Need It. You will need to pick the right tools for the right job. If you re just hanging up a picture on the wall, a hammer and nail will probably do the trick, but if you re totally reconstructing your kitchen from the ground up, you will definitively need other heavier equipment. Software development is not much different when it comes to tooling. Track your items. But, always start with tracking the progress of your work items from day one in the project, whether these are user stories, features, smart use cases, chores, bugs or whatever you re doing. This way you at least have data on your project from the start, and you can start counting and measuring at any point in time, which you will want eventually. Yes, Excel. As developers, you might have a big grudge against Microsoft Excel (probably because it s the favorite tool for project managers), but I ve seen many projects that have used a single (not a hard disk full of them) spread sheet successfully to keep track of everything you will need to know in your project. Think of burn-down or burn-up charts, retrospective outcomes, velocity numbers and current work item statuses. Only add when needed. When starting with a minimal set of tools in your project, for some reason, at some point in time, you will realize that some element of your work could be optimized by using a tool. Usually you start to realize this during a sprint s retrospective. Only now figure out what you ve been missing, identify the minimal set of requirements for a-tool-to-add, and then simply choose the cheapest and best fitted product to add to your eco-system. And this tool doesn t have to be the absolute best or most bling-bling magazine voor software development 11

GENERAL rich tool you can find on the planet. Just keep it simple. Don t get religious. Many people in the Scrum community feel that it is mandatory to use user stories as their unit of work. However, if you consider the Scrum Guide, the phrase user story does not even appear in it. With tooling, it s not much different. I ve had many discussions with people who feel very religious about one tool or another. Jira is a good example. There s nothing wrong with Jira, but it is by far to be considered mandatory in Scrum projects. Nor is it mandatory to even use an online issue tracking tool. Especially when the whole team is in the same room. Agile tools don t exist. Also, don t let others judge which tools you can or cannot use in your agile project. Or even worse, which tools are agile or are not agile. In my humble opinion, there s no such thing as an agile tool. Tools are either fitted to your project, or are not fitted. I ve been using UML modeling tools and model driven development tools successfully in many agile projects, despite the fact that some zealots think it is not agile to use such tools. No free lunch. There s no such thing as a free lunch. Every tool comes with consequences. Post-its will eventually fall off the wall. Free online dashboard tools are often limited to a certain number of users, of are only available for free for a certain period of time. Frameworks will version. Make sure you realize the consequences. By default, any automated tool you add will create a (vendor) lock-in. To wrap things up, I m sorry that I m unable to give you more concrete tooling recommendations. Every project is different, and agile is a sliding scale. Personally, I consider it wise to start small, and only add tools when the add value, instead of starting with a fully integrated application lifecycle management support eco-system. And at last, always keep in mind that a fool with a tool is still a fool. The same goes for agile fools with tools. Sander Hoogendoorn Sander Hoogendoorn houdt zich in zijn rol als Principal Technology Officer bij Capgemini zich vooral bezig met de innovatie van software development. Sander is erkend global thought leader op het vlak van agile development bij Capgemini. Daarbij is hij onder meer verantwoordelijk voor Capgemini s agile ontwikkelplatform, dat het Accelerated Delivery Platform (ADP) wordt genoemd. Als je voorop wilt lopen in je vakgebied, sluit je je aan bij de SDN community! Als lid van het Software Development Network, kortweg het SDN, ben je op de hoogte van de nieuwste ontwikkelingen. Je maakt deel uit van een netwerk van professionele architecten, designers en ontwikkelaars die elkaar met raad en daad terzijde staan. Je kunt lid worden van het SDN op basis van een bedrijfs- of een persoonlijk lidmaatschap. Het belangrijkste verschil is dat bij een bedrijfslidmaatschap twee of meer personen recht hebben op gratis toegang tot bijeenkomsten en de SDN website, bij een persoonlijk lidmaatschap is dat één persoon. De kosten voor een bedrijfslidmaatschap voor twee personen bedragen 299,- per jaar. Uitbreiding kost 99,- per persoon per jaar. Een persoonlijk lidmaatschap kost 199,- per jaar. Bedragen zijn exclusief 21% BTW. (Bedrijfs)naam : T.a.v. Adres PC/Woonplaats Email adres Telefoonnr. Personen (indien bedrijfsabonnement minimaal 2) Een lidmaatschap wordt aangegaan voor tenminste één jaar en wordt zonder schriftelijke annulering automatisch met een jaar verlengd op 31 december. Facturering vindt plaats per kalenderjaar of een deel hiervan. Opzeggingen uitsluitend schriftelijk en tenminste zes weken voor het einde van het jaar. Datum Handtekening Verzend dit formulier naar het secretariaat van SDN: Postbus 506 7100 AM Winterswijk of mail naar info@sdn.nl 12 MAGAZINE

DELPHI Bob Swart Delphi REST Client Components Apart from the REST Debugger, covered in the previous issue of SDN Magazine, Delphi also contains a number of REST Client components: TRESTClient, TRESTRequest, TREST Response, and TRESTResponseDataSetAdapter, plus four REST authentication components: TSimpleAuthenticator, THTTPBasicAuthenticator, TOAuth1Authenticator, and TOAuth2 - Authenticator. All these new components can be found in the REST Client category of the Tool Palette: REST Twitter Client The example of this article covers the implementation of a REST Twitter Client, connecting to our Twitter account, and being able to send new Tweets. All using the Delphi REST Client components, including one of the Authenticator components (the TOAuith1Authenticator). We start by going to the Twitter API website, were we need to register our application (or at least specify that an application will be accessing the Twitter API). The URL for this is https://apps.twitter.com/ and we must login with our normal Twitter account. Here, we can click on the "Create new App" button to create a new application that will be allowed to communicate with the Twitter API. In the new form, we must specify the details: Fig. 1 The TRESTRequest is the component that makes the actual RESTRequest. It has a Client property (pointing to a TRESTClient) as well as a Response property (pointing to a TRESTResponse component, introduced shortly). Using the TRESTRequest component we can specify the Resource property as well as the Params TRESTRequestParameterList collection. The TRESTResponse component is the one that will receive the response of a connected TRESTRequest component in the Content property. Fig. 2 In our case, I've specified a name of "Delphi Developer Days 2014 Demo" with a description of "Delphi Developer Days 2014 Demo for the RAD Development and REST Client Tools (REST Debugger) session". As website I've used my own URL at http://www.drbob42.com although I probably should have used http://www.delphideveloperdays.com as well. magazine voor software development 13

DELPHI Now, click on "Create your Twitter Application", which will give you some more information, like the Access Level, etc. This is the page were we can find some of the initial keys for use with the OAuth authentication to ensure our application has the permission granted to use the Twitter API via REST. Let's take a look at the response. Place a TRESTResponse component (and make sure TRESTRequest Response property points to TRESTResponse now). In de FormCreate, write the following: RESTRequest.Execute; if RESTResponse.GetSimpleValue('oauth_token', LToken)then OAuth1Authenticator1.RequestToken := LToken; if RESTResponse.GetSimpleValue('oauth_token_secret', LToken) then OAuth1Authenticator1.RequestTokenSecret := LToken; Add REST.Authenticator.OAuth.WebForm.Win to the uses clause, and as a result we will see a page where we need to login using out Twitter account, and click on the "Authorize App" button in order to get a PIN code that needs to be used in the next part of our application. Fig. 3 Click on the "Manage API Keys" link to go to a screen with your API Key and API secret key values. You need to copy the API Key and API secret values, and place them in the comments in your Delphi REST Client project, so we can use them shortly. OAUTH In order to be allowed to access the Twitter API using REST, we need to use an OAuth1Authenticator component, so place one on the form. We must set three EndPoint properties to the specific URLs for retrieving the Access Token, the Request Token and for doing the actual Authentication. These properties can be specified in the Object Inspector as follows: AccessTokenEndpoint to https://api.twitter.com/oauth/access_token RequestTokenEndpoint to https://api.twitter.com/oauth/request_token AuthenticationEndpoint to https://api.twitter.com/oauth/authenticate Finally, we need to set the CallbackEndpoint property value to oob. Then, set the ConsumerKey and ConsumerSecret property values based on the values from your twitter registered page (the two values you just copied from the web browser). Now, we can place a TRESTClient component on the form (it will automatically connect its Authenticator property to the OAuth1Authentocator1 component). Set the BaseURL to the RequestTokenEndPoint, or https://api. twitter.com/oauth/request_token Then, place a TRESTRequest component on the form, and set the Method to mtpost (was mtget) Right-mouse click on RESTRequest and do Execute... This will give Response: 200 - HTTP/1.0 200 OK Fig. 4 To simplify things, I've written the FormCreate event handler that will perform the first two steps, as well as a FormShow that will be used to enter the PIN code (which cannot be copied over the clipboard, so I'm using a InputBox to ask the user to specify the PIN code manually. uses REST.Types, REST.Authenticator.OAuth.WebForm.Win; procedure TFormMain.FormCreate(Sender: TObject); var wv: Tfrm_OAuthWebForm; LToken: String; begin RESTClient1.Authenticator := OAuth1Authenticator1; RESTRequest1.Client := RESTClient1; RESTRequest1.Response := RESTResponse1; OAuth1Authenticator1.RequestTokenEndpoint := 14 MAGAZINE

DELPHI 'https://api.twitter.com/oauth/request_token'; OAuth1Authenticator1.AuthenticationEndpoint := 'https://api.twitter.com/oauth/authenticate'; OAuth1Authenticator1.AccessTokenEndpoint := 'https://api.twitter.com/oauth/access_token'; OAuth1Authenticator1.CallbackEndpoint := 'oob'; // get your consumer key & secret from https://api.twitter.com OAuth1Authenticator1.ConsumerKey := 'QjCB98tSX25CUpjDXLm3Sw'; OAuth1Authenticator1.ConsumerSecrect := 'J0fs8uaLcRCau8S1ujtG04jtkdOKNVwvSgFQfliTek8'; RESTClient1.BaseURL := OAuth1Authenticator1.Request- TokenEndpoint; RESTRequest1.Execute; if RESTResponse1.GetSimpleValue('oauth_token', LToken) then OAuth1Authenticator1.RequestToken := LToken; if RESTResponse1.GetSimpleValue('oauth_token_secret', LToken) then OAuth1Authenticator1.RequestTokenSecret := LToken; // Get the auth-verifier (PIN must be entered by the user!) wv := Tfrm_OAuthWebForm.Create(self); try wv.showmodalwithurl(oauth1authenticator1.authenticationendpoint + '?oauth_token=' + OAuth1Authenticator1.RequestToken); finally wv.release; end; end; At this point, the OAuth Web Login page will be shown, where we need to log into our Twitter account, and click on the "Authorize App" button in order to get a PIN code. The next step, in the FormShow, will start by getting the PIN code (using the Clipboard, which works on some machines, or the InputBox method), and preparing the REST components so we can make a call to the Tweet API. procedure TFormMain.FormShow(Sender: TObject); const {$J+ Done: Boolean = False; var LToken: String; begin if not Done then begin Done := True; OAuth1Authenticator1.VerifierPIN := InputBox('PIN','PIN:',''); RESTClient1.BaseURL := OAuth1Authenticator1.AccessTokenEndpoint; RESTRequest1.Params.AddItem('oauth_verifier', OAuth1Authenticator1.VerifierPIN, TRESTRequestParameterKind.pkGETorPOST, [TRESTRequestParameterOption.poDoNotEncode]); RESTRequest1.Execute; if RESTResponse1.GetSimpleValue('oauth_token', LToken) then OAuth1Authenticator1.AccessToken := LToken; if RESTResponse1.GetSimpleValue('oauth_token_secret', LToken) then OAuth1Authenticator1.AccessTokenSecret := LToken; /// now we should remove the request-token OAuth1Authenticator1.RequestToken := ''; OAuth1Authenticator1.RequestTokenSecret := ''; OAuth1Authenticator1.CallbackEndpoint := ''; end; end; Note that I'm using a typed constant called Done to ensure that the code inside the FormShow to include the PIN is executed only once, and not for each and every FormShow call. Finally, the code to send an actual tweet is done by sending a status update to the Twitter API, as follows: procedure TFormMain.Button2Click(Sender: TObject); begin RESTClient1.BaseURL := 'https://api.twitter.com'; RESTRequest1.Resource := '1.1/statuses/update.json'; RESTRequest1.Params.AddItem('status', edtweet.text, TRESTRequestParameterKind.pkGETorPOST); RESTRequest1.Execute; end; Although it may take quite some code, the result is that our custom application can work with the Twitter API and send REST requests using the Delphi REST Client components. Bob Swart Bob Swart is werkzaam in de IT sinds 1983 en heeft daarbij een voorliefde voor (Turbo) Pascal en Delphi. Bob spreekt regelmatig op (internationale) conferenties over Delphi en heeft honderden artikelen geschreven, alsmede zijn eigen Delphi cursusmateriaal voor Delphi trainingen en workshops. Behalve voor het geven van trainingen, is Bob ook beschikbaar voor consultancy, coaching, ontwerp- en bouwwerkzaamheden, of andere ondersteuning op het gebied van software ontwikkeling met Delphi - voor zowel Win32 als.net. Sinds de zomer van 2007 is Bob ook reseller van CodeGear producten zoals Delphi en RAD Studio. Bob Swart Training & Consultancy is gevestigd in Helmond Brandevoort, en beschikt over een eigen trainingsruimte van ruim 42 vierkante meter, inclusief een testlab voor alle mogelijke toepassingen. De voorliefde van Bob voor Pascal en Delphi heeft hij ook tot uiting laten komen in de namen van zijn kinderen Erik Mark Pascal en Natasha Louise Delphine. magazine voor software development 15

.NET Piet Amersfoort Async/await en het restaurantmodel Async/await is de belangrijkste uitbreiding van C# in versie 5.0. Deze uitbreiding is toegevoegd om asynchroon programmeren te vereenvoudigen. Het aantal regels code wordt bij toepassing van async/await verminderd en de kans op fouten wordt kleiner. Maar het is ook een extra abstractie laag. Een programmeertaal is per definitie een abstractie die het manipuleren van nullen en enen vereenvoudigd. Maar abstractie op abstractie kan het zicht op wat er werkelijk gebeurt vertroebelen. Hierdoor wordt programmeren gereduceerd tot het toepassen van recepten. Bij problemen is het dan niet meer mogelijk zorgvuldige analyses te maken en uiteindelijk de juiste oplossing te kiezen. Om beter inzicht te krijgen in deze abstracties, zullen we naar de bedrijfsvoering van een restaurant kijken en deze vergelijken met de ontwikkeling van asynchroon programmeren in C#. We kunnen dan iets beter begrijpen op welke wijze async/await problemen voor ons oplost. Simpel voorbeeld van async/await Stel dat we WPF-applicatie willen ontwikkelen die onder andere een window bevat met daarop een invoerveld, een button en een label. Als de gebruiker een priemgetal invoert en op de button klikt, verschijnt op het label de tekst: het ingevoerde getal is een priemgetal. Als er iets anders wordt ingevoerd, verschijnt er een andere tekst. private void Button_Click(object sender, RoutedEventArgs e) { Resultaat.Content = IsPriemWrapper(InputText.Text); private string IsPriemWrapper(string inputtext) { long getal; bool isgetal = long.tryparse( inputtext, out getal); if (isgetal) { return IsPriem(getal)? inputtext + " is een priemgetal" : inputtext + " is niet een priemgetal"; return inputtext + " is niet een getal"; private bool IsPriem(long getal) { for (var i = 2; i < getal; ++i) { if (getal % i == 0) return false; return true; Listing 1: Is een getal een priemgetal? (synchrone versie) Als er kleine getallen worden ingevoerd, dan verschijnt de tekst bijna direct. Bij grote getallen, bijvoorbeeld 756771235126757131, duurt de berekening veel langer. Tijdens de berekening kan de gebruiker de applicatie niet meer gebruiken, het scherm is bevroren. Bij Windows desktop applicaties is dit gedrag niet fraai, voor Silverlight en Windows Store Apps is dit gedrag niet acceptabel. De killer feature van versie 5.0 van C# is async/await. Het is aan de taal toegevoegd om asynchroon programmeren voor ontwikkelaars makkelijker te maken. Dus laten we het nieuwe async/await-recept gebruiken: maak van de langzame functie een functie die een Task<TResult> oplevert, in ons geval een Task<string>. Roep deze functie aan met await. Maak de UI-functie async. Nu is de aanroep van de functie asynchroon geworden. Als de gebruiker op de knop heeft geklikt, dan blijft de applicatie reageren. private async void Button_Click( object sender, RoutedEventArgs e) { Resultaat.Content = await IsPriemWrapperAsync( InputText.Text); private Task<string> IsPriemWrapperAsync( string inputtext) { 16 MAGAZINE

.NET return Task.Run(() => IsPriemWrapper(inputText)); Listing 2: De aanpassingen om een asynchrone versie van de functie te maken Door deze simpele aanpassing voldoet het programma aan onze eisen en is de code ook te gebruiken in bijvoorbeeld een Windows Store App. Onder de C#-motorkap is veel complexe software verstopt. Het verbergen van complexiteit is in het algemeen een goede zaak. Als we een auto besturen, is het niet noodzakelijk dat we alle details van de werking van de automotor kennen. Het is zelfs aan te raden om tijdens het rijden hierover niet te veel na te denken. Maar, om met Einstein te spreken, maak het simpel, maar niet te simpel. Als onze software complexe scenario s moet ondersteunen, dan is het goed om te weten wat er zich onder de motorkap bevindt. We gaan daarom asynchroon programmeren beschrijven door deze te vergelijken met de bedrijfsvoering van een restaurant. Restaurant 0.0 We hebben de gehele dag achter het toetsenbord doorgebracht en hebben trek in een stukje pizza. We gaan naar Pizzeria Francesco. In deze zaak werkt Francesco en hij is de eigenaar, de ober en de kok. Hij is de enige medewerker. We bestellen een pizza en een flesje wijn Fonterutoli Chianti Classico uit 2005. Francesco begint aan de voorbereidingen van de pizza en gaat in de wijnkelder op zoek naar de gevraagde wijn. Hij komt niet meer terug. Na een uur wachten vertrekken we maar. De werkwijze van dit restaurant is te vergelijken met de manier waarop de eerste pc s werkten. Alle opdrachten worden in de gevraagde volgorde uitgevoerd. Als er één of meerdere opdrachten tussen zitten die lang duren, dan kan de gebruiker verder niets meer doen. En als opdrachten zelfs oneindig lang duren en dus geen resultaat opleveren, dan is er geen manier om het programma te stoppen en door te gaan met andere opdrachten. De enige optie is dan de computer uit te zetten, vervolgens weer aan te zetten en nieuwe opdrachten te starten. Ditmaal zonder de opdracht die het probleem heeft veroorzaakt. Restaurant 1.0 We waren niet de enige klanten van het restaurant die problemen hebben gehad. Meerdere klanten zijn boos weggelopen en de zaak was bijna failliet. Francesco heeft daarom een nieuwe manier van werken bedacht. Hij schrijft de opdrachten op kaartjes die hij bundelt met een touwtje. Hij werkt vijf minuten aan het eerste stapeltje met opdrachten, dan gaat de wekker. Hij stopt met zijn werk en pakt het volgende stapeltje en gaat verder met de opdrachten van het nieuwe stapeltje. Na vijf minuten gaat de wekker weer en gaat Francesco verder met de volgende stapel opdrachten. Als hij vijf minuten aan de opdrachten van het laatste stapeltje heeft gewerkt, gaat hij met de opdrachten van de eerste stapel verder. Deze manier van werken is minder efficiënt, omdat Francesco om de vijf minuten van stapel moet wisselen. Als hij een pizza aan het bakken is, moet hij daarmee stoppen en met een andere opdracht verder gaan, bijvoorbeeld het serveren van wijn. Hij moet zich hiervoor omkleden, kokskleding uit, oberkleding aan en verder met de volgende opdracht. Maar het originele probleem is wel opgelost. Als Francesco aan het zoeken is naar een fles wijn die hij moeilijk of niet kan vinden, dan gaan de andere opdrachten door en krijgen uiteindelijk alle gasten eten geserveerd. Dit is de wijze waarop single core Windows pc s werken. Threads zijn setjes instructies die bij elkaar horen, per thread worden de instructies uitgevoerd. Als er meerdere threads zijn aangemaakt, worden deze semi-parallel uitgevoerd. Elke thread krijgt geregeld de tijd om een gedeelte van zijn instructies uit te voeren. In.NET 1.0 is het mogelijk zelf nieuwe threads aan te maken. Deze kunnen worden gestart, waarna de opdrachten semi-parallel worden uitgevoerd. Een nadeel van deze wijze van werken is al genoemd. Het wisselen van thread s kost veel tijd en is daardoor inefficiënt. Er zijn meer nadelen, het aanmaken van nieuwe threads is duur. In termen van het restaurantmodel, het kost Francesco veel tijd en energie om een nieuwe stapel opdrachten aan te maken. Hij moet op zoek naar een nieuwe stapel lege kaartjes, de instructies opschrijven en ze daarna bundelen. Dan moet hij een nieuw stukje touw zoeken en dit door de bundel met opdrachten rijgen. Er is nog een overeenkomst tussen het werk in het restaurant en de meeste Windowsapplicaties. Als Francesco een opdracht moet uitvoeren waarbij er contact is met de gasten, dan zal hij dit als ober moeten doen en niet als kok. Windows Forms en WPF applicaties vereisen het gebruik van STAThreadAttribute. STA is de afkorting van single-threaded apartment. Er mogen wel meerdere threads gebruikt worden in de applicatie, maar er is maar één UI-thread en het is de taak van deze thread de UI aan te passen. Tegen sluitingstijd zijn er nog stapeltjes met opdrachten waaraan gewerkt wordt. Sommige van deze opdrachten kunnen direct gestopt worden, bijvoorbeeld het afbakken van stokbrood voor nieuwe gasten. Andere opdrachten moeten wel worden afgemaakt, bijvoorbeeld de vuile borden in de vaatwasmachine zetten en de machine aanzetten voor het weggaan. Daarom heeft Francesco twee kleuren touwtjes, zwart en rood. Stapels met het zwarte touwtje mogen worden gestopt en stapels met een rood touwtje moeten worden afgemaakt, voordat de zaak kan worden afgesloten. Zo zijn er ook twee soorten threads, background threads en foreground threads. Anders dan de naam suggereert heeft het onderscheid niets te maken met de UI. Het enige verschil is de wijze van stoppen, voor de rest zijn ze identiek. Als een applicatie wordt gestopt, worden de background threads direct gestopt. De foreground threads moeten eerst hun werk afmaken. Call-backs Francesco wacht ook veel, ondanks dat hij het razend druk heeft. Hij stopt het eten in de oven en dan wacht hij de resterende vijf minuten. Daarna gaat hij door met de andere stapeltjes opdrachten. Daarna is weer het stapeltje met het eten in de oven aan de beurt. Dan wacht hij weer vijf minuten en gaat daarna weer door met zijn andere werk. Hij denkt: dat moet anders kunnen. Als hij nu eens twee stapeltjes met opdrachten maakt: één stapeltje met opdrachten tot het eten in de oven gaat en één stapeltje met opdrachten voor als het eten in de oven klaar is. Hij kan dan eerst de voorbereiding uitvoeren, vervolgens het eten in de oven zetten en de stapel met de vervolgopdrachten op een speciale plaats leggen. Nu hoeft hij niet meer nodeloos te wachten. Als het eten in de oven klaar is, gaat hij verder met de klaargelegde stapel vervolgopdrachten..net bevat sinds versie 1.0 classes die het BeginInvoke/EndInvokdepattern implementeren. Dit wordt het Asynchronous Programming Model (APM) genoemd. Als voorbeeld bekijken we de opdracht om in een groot tekstbestand op een netwerklocatie te controleren of er een bepaald woord in voorkomt. Deze opdracht kan synchroon worden uitgevoerd, maar dan moeten we net als Francesco nodeloos wachten totdat alle gegevens zijn verplaatst naar onze pc. Als we deze opdracht asynchroon uitvoeren, kunnen we de controle en de presentatie van het resultaat in een functie vastleggen. Deze functie wordt een call-back-functie genoemd. De functie wordt aangeroepen als de voorgaande activiteiten zijn uitgevoerd. magazine voor software development 17

.NET Als we de BeginRead/EndRead methodes van de Stream class gebruiken, kunnen we de BeginRead gebruiken om de informatie te verzamelen en de EndRead om de controle uit te voeren en het resultaat te presenteren. We wachten dan niet actief op het resultaat van de lees- en controleactie, maar we vertellen wat er met het resultaat moet gebeuren en vertrouwen erop dat dit uiteindelijk door een andere thread zal worden uitgevoerd en het resultaat ook zal worden getoond. Bij asynchroon programmeren zijn er drie soorten opdrachten. Opdrachten die voornamelijk door de hoofd processor (CPU) worden uitgevoerd. Deze opdrachten worden CPU bound genoemd en kunnen verder worden verdeeld in kortlopende en langlopende opdrachten. Opdrachten die lang duren worden long running operations genoemd. Het eerste voorbeeld, de bepaling of een getal een priemgetal is, is een voorbeeld van een kortlopende opdracht bij kleine invoerwaarden en een langlopende berekening bij grote waarden. Daarnaast zijn er de opdrachten die hoofdzakelijk niet door de hoofd processor worden uitgevoerd. Deze worden I/O bound opdrachten genoemd. Voorbeelden hiervan zijn lees- en schrijfopdrachten naar de harddisk en netwerkcommunicatie. Elk soort opdracht vereist een andere benadering. Kortlopende opdrachten kunnen beter synchroon worden uitgevoerd, langlopende opdrachten kunnen het beste op een andere thread dan de UI-thread worden uitgevoerd en I/O bound taken kunnen het beste worden gesplitst in deeltaken. Door ons tijdens het programmeren bewust te zijn van dit onderscheid, kunnen we altijd de juiste oplossing kiezen. Voorbereiding Het aanmaken van threads is duur. Franseco moet op zoek naar nieuwe kaartjes, een pen, touw, etc.. En altijd op momenten dat hij zijn tijd wel beter kan gebruiken, want de nieuwe gasten komen binnen, moeten naar hun tafel worden gebracht, de pizza s moeten worden gebakken. Daarom heeft Francesco bedacht dat het handig zou zijn als hij al stapeltjes met kaarten heeft klaarliggen. En hij heeft ook bedacht dat het misschien verstandig zou zijn om de stapeltjes waar hij mee klaar is niet in de vuilnisbak te gooien, maar her te gebruiken. Hij is daarom naar de Makro gegaan en heeft daar een doos opschrijfboekjes gekocht, zodat hij de opdrachten kan vastleggen. In.NET 1.0 is het idee van Francesco geïmplementeerd. Het heet de threadpool. Als de applicatie wordt opgestart, dan worden er al enkele threads aangemaakt. Deze doen nog niets, maar als het nodig is kunnen ze aan het werk. Door de statische methode ThreadPool.QueueUserWorkItem worden ze in de wachtrij gezet om te worden uitgevoerd door de threads. Als laatste heeft Francesco bedacht dat het aantal stapeltjes met opdrachten waaraan hij gelijktijdig werkt, moet worden beperkt. Hij gaat pas aan de slag met een nieuwe stapel als hij een andere stapel met opdrachten heeft afgehandeld. Dit heeft diverse voordelen, onder andere dat als mensen besteld hebben, zij relatief snel eten op tafel krijgen. Aan de stapels met opdrachten die in behandeling zijn wordt relatief vaak gewerkt. Ook de threadpool heeft een beperkte capaciteit. Alle opdrachten worden wel aangenomen (QueUserWorkItem), maar wanneer er daadwerkelijk gestart wordt met de uitvoering van de opdracht, wordt onder andere bepaald door het aantal WorkItems waar al aan wordt gewerkt. Restaurant 2.0 Soms weet Franseco tijdens het voorbereiden van zijn opdrachten nog niet wat er moet gebeuren als het eten uit de oven komt. Dan is het misschien voldoende om het eten uit de oven te halen en te laten weten dat het klaar is. Dan kan hij daarna bepalen wat er met het eten moet gebeuren. In versie 2.0 van.net komt er een tweede asynchroon pattern bij. Dit heet Event-based Asynchronous Pattern (EAP). In plaats van een callback is er een event. Alle methodes die geïnteresseerd zijn in het event kunnen hierop reageren. Daarnaast was er nog het probleem dat de UI door de juiste thread moet worden geüpdatet. Dat werd in.net 2.0 versimpeld door helper-class de SynchronizationContext te implementeren. In termen van het restaurantmodel, de SynchronizationContext onthoudt in welke stapel de klantcontacten zich bevinden, en weet als er informatie beschikbaar komt, hoe deze moet worden doorgespeeld naar de klanten. De SynchronizationContext bezit een Post-methode en accepteert een callback. Restaurant 3.0 Francesco heeft zijn restaurant steeds beter onder controle. Het aantal klanten groeit elk jaar. Ook stijgt elk jaar de productiviteit van Francesco. Door harder te werken kan hij de groei in zijn eentje bijbenen. Echter op een dag is de rek eruit. Aan het eind van de avond heeft hij het zo warm en is hij zo vermoeid, dat hij naar de dokter gaat. Van de dokter moet hij het rustiger aan gaan doen. De dokter stelt voor dat hij een medewerker aanneemt. Op deze wijze kan zijn restaurant doorgroeien. Zijn manier van werken met de stapeltjes opdrachten maakt dit al mogelijk. Taken opsplitsen is nu nog belangrijker. Door deze te splitsen kan het werk worden verdeeld en sneller worden uitgevoerd. Ook de CPU s in onze pc s zijn door de jaren heen steeds sneller geworden. Maar door de processoren sneller te maken, worden deze ook warmer en uiteindelijk worden ze te warm. Dus het sneller maken van de procesoren is gestopt. In plaats daarvan worden nu in een pc meerdere CPU s geplaatst. De noodzaak om het werk eerlijk te verdelen over de CPU s vereist dat bij het ontwikkelen van software opdrachten worden gesplitst, zodat elke CPU kan bijdragen aan het uitvoeren van de opdrachten. Restaurant 4.0 Nu Francesco meerdere medewerkers heeft, is het dus noodzakelijk het werk beter te verdelen. Om dit efficiënter te laten werken, heeft hij een pc aangeschaft. Hierin kan hij de stapeltjes met opdrachten vastleggen en beheren. Hij heeft een abstractie laag toegevoegd. Francesco werkt niet meer met fysieke kaartjes, maar met een elektronische versie van de kaarten. In.NET 4.0 is de Task Pararlel liibrary (TPL) toegevoegd. Dit wordt gezien als een abstractie laag over de al aanwezige threads. Het abstractie moet asynchroon ontwikkelen gemakkelijker maken. Een taak die de TPL ons uit handen neemt, is nadenken over de vraag of we zelf een thread moeten maken of de theadpool-thread moeten gebruiken. De TPL regelt dit voor ons. Waar blijft het eten? Als we voor een theatervoorstelling nog iets willen eten in Restaurant Francesco, dan moeten we op tijd eten geserveerd krijgen en op tijd vertrekken om niets van de voorstelling te missen. Als er vlak voor het vertrek nog geen eten op tafel staat, willen we graag weten hoe lang het nog gaat duren. Als het antwoord is: het kan nog wel even duren dan willen we de mogelijkheid hebben om onze bestelling te annuleren. Als we met een groep mensen in een restaurant uit eten zijn en iedereen heeft zijn eten opgediend gekregen op één persoon na, dan zullen we wachten met eten. Stel nu dat het eten van die persoon is aangebrand en in de vuilnisbak is verdwenen, dan willen we dit wel graag weten. Dan kunnen we het vervolg zelf bepalen. Dat we nog even wachten met eten of dat we alvast beginnen of dat we het eten op tafel verdelen. 18 MAGAZINE

.NET Voor de service van restaurants is het essentieel dat bovenstaande processen goed worden ondersteund. In essentie bevatten deze processen de volgende elementen: Prioriteiten (priority) Voortgangsbewaking (progress reporting) Foutafhandeling (exception handling) Beëindigen (cancellation) Vervolgacties of basis van status (callbacks en continuations) Deze elementen worden in de TPL geïmplementeerd. De TPL is een zeer uitgebreide bibliotheek. Helaas ontbreekt hier de ruimte om deze functionaliteit in detail te bespreken. Restaurant 5.0 Francesco is hard aan het werk in zijn restaurant en maakt inmiddels veelvuldig gebruik van het nieuwe systeem. Echter op dagen dat hij niet aanwezig is, wil hij wel dat de zaken doorlopen. De training van medewerkers kost veel tijd. Daarnaast vereist het werken met het systeem veel extra handelingen. Daarom verzucht Francesco weleens: kan het niet wat simpeler? Is het niet mogelijk te werken op een manier die lijkt op de oude eenvoudige manier van werken? We zijn aangekomen bij C# 5.0 met async/await. Aan het voorbeeld van het begin kunnen we zien dat asynchrone code lijkt op de originele synchrone code. Achter de schermen wordt er echter gewerkt met taken uit de TPL, threads uit de threadpool, de synchronisatie context en call-backs. De compiler vertaalt de relatief simpele code in een complexe state machine. Dit gebeurt allemaal om het asynchroon programmeren voor ons zo makkelijk mogelijk te maken. Tot slot Dit artikel is een korte introductie van asynchroon ontwikkelen in C# en async/await. Dit kijkje in de keuken heeft hopelijk tot meer inzicht geleid en smaakt mogelijk naar meer. Ik verwijs om die reden graag naar enkele van de vele boeken waarin veel dieper op de materie wordt ingegaan: Bart de Smet - C# 5.0 Unleashed Joseph Albahari, Ben Albahari - C# 5.0 in a Nutshell Richard Blewett, Andrew Clymer- Pro Asynchronous Programming with.net John Skeet - C# in Depth, Third Edition Piet Amersfoort Piet Amersfoort is zelfstandig consultant op het gebied van bedrijfsautomatisering. Hij is op zoek naar problemen die meer zijn dan een uitdaging. Microsoft-technologieën is één van zijn specialisme. Zijn blog is te vinden op: http://ps-a.blogspot.com/ Microsoft Azure Websites Backup Als je een Microsoft Azure Website hebt gemaakt, dan kun je deze nu ook regelmatig en automatisch laten backuppen. In deze backup routine kun je ook de bijbehorende database of linked resources meenemen. Dit maakt Microsoft Azure websites nog interessanter om te gebruiken. magazine voor software development 19

DELPHI Cary Jensen Talking About Frames I like frames, and by frames, I mean instances of the TFrame class first introduced in Delphi 5. I use frames extensively in my applications, and I was very pleased to see frames added to the FireMonkey component library. In this article I want to share with you a couple of techniques that I have been using extensively in my applications. Of course, these techniques apply to frames. I will also share with you an observation about mobile development with Delphi, and will argue that frames solve one of the bigger issues that Delphi mobile developers have been commenting about. But first, let's start with a brief overview of frames. Frames in a Nutshell A frame is a type of module, much like a form or data module. Frames support a design surface for both visual and non-visual components, and in that way are similar to forms. (Data modules also support a design surface, but that design surface is limited to non-visual components.) From a user interface perspective, frames are significantly different from forms. Specifically, forms are intended to be used as a whole, defining a window or dialog box in your application. Frames, by comparison, are more like a fancy panel. Specifically, one or more frames can be placed onto a form, and frames can even be placed into other frames. In this way a frame acts like a compound component. That is, a component that exposes other components, both visual and nonvisual. Unlike a traditional compound component, however, frames permit the objects that have been placed into them to be selected and configured using the Object Inspector from the container in which the frame is placed. For example, I can create a frame that contains a DBGrid and a DataSource. Once I place that frame onto a form, I can still select the DataSource in the frame instance and set its DataSet property. Similarly, from the form on which this frame appears I can easily reach into the frame and change the position of the DBGrid, changing the position in just this one instance of the frame. Permitting the objects that are exposed by a compound component to be configured in this fashion would require custom code, and some of that could get quite complicated. There are two primary benefits of frames. The first is that, under certain circumstances, frames can dramatically reduce the amount of resources that need to be stored in a project. The second, and in most cases the more important benefit, is that frames permit you to visually create objects that can be easily reused and extended. To look at it another way, frames are building blocks for your user interface that can be easily configured at design time. Components that appear on your frame are available in each instance of the frame that you place, and the code defined for the frame is likewise present in each instance. As a result, frames are a powerful tool in an object-based design environment like Delphi, and an excellent source of code reuse. Before I continue, let me acknowledge that it is also possible to make a form appear within another form, and a given form can consist of one or more embedded forms. However, doing so is a strictly runtime operation, and it requires several lines of code, at a minimum. You cannot place one form within another at design time. Furthermore, many of the features of a form, such as its OnClose event handler, are lost when placed on another form. As a result, using a form within a form more or less turns the form into a frame, but without the convenience of design time placement and configuration. Dynamically Created Frames In my brief introduction to frames I emphasized the benefits of the design time use of frames. And while those benefits are very real, they tend to make us forget that frames can play an important role in runtime code. In particular, I often find myself creating frames dynamically at runtime, and the results are something that would be difficult using any other technique. For example, I often have the need to create a calendar-like interface to represent date-related events, such as the daily schedules of work to be performed over the course of a month. In those cases, I create a frame that represents a day, and that frame is designed to display the schedule for a date. At runtime I create one instance of the frame for each day in the month, positioning the frame on the calendar based on its day of the month and day of the week. I might even make each instance of the frame responsible for loading the schedule for the particular date that it represents. Creating a calendar-like interface at design time cannot entirely capture the fluid nature of a calendar. For example, some months have more days than others, and one month (February) occasionally has an extra day, depending on the year. In addition, the first day of the month can potentially land on any day of the week. In other words, the layout of the calendar depends on which month and which year the calendar interface is representing. Dynamically-created frames provide an excellent, object-oriented solution to this interface problem. A calendar is really just one example of dynamic frames. Instead of calendar dates, I might create a frame that represents a team of employees. Since the total number of teams, and the specific employees that constitute each team, may vary from day to day, it is unreasonable to design an interface that displays all teams in advance. Here again, a query to a database can identify the number teams as 20 MAGAZINE