MAGAZINE. IN DIT NUMMER O.A.:

Maat: px
Weergave met pagina beginnen:

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

Transcriptie

1 MAGAZINE IN DIT NUMMER O.A.: MP3 Tags in Delphi 2009 < Peer to Peer Applicaties met.net Framework 3.5 < Zeven Windows Communication Foundation Best Practices < Portaltechnologie: SharePoint of DotNetNuke? < The Best of Both Worlds: Modernizing Core Sytems < Nummer 99 November 2008 SDN Magazine verschijnt elk kwartaal en is een uitgave van Software Development Network 99

2 Advertentie Giraffe

3 Colofon Uitgave: Software Development Network Postbus AM Winterswijk tel fax Zeventiende jaargang No. 99 november 2008 Bestuur van SDN: Remi Caron, voorzitter Rob Suurland, penningmeester Joop Pecht, secretaris Mark Vroom, vice-voorzitter Redactie: Rob Willemsen Aan dit magazine werd meegewerkt door: Maurice de Beijer, Mark Blomsma, Rolf Craenen, Marcel van Kalken, Stefan Kamphuis, Marcel Meijer, Johan Parent, Joop Pecht, Maarten van Stam, Bob Swart, Louis vd Tol, Robertjan Tuit, Marianne van Wanrooij, Rob Willemsen en natuurlijk alle auteurs! Listings: Zie de website voor eventuele source files uit deze uitgave. Vormgeving en opmaak: Reclamebureau Bij Dageraad, Winterswijk 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 Giraffe 2 Bergler Nederland b.v. 10 Logica 18 4DotNet b.v. 22 Microsoft Avanade 41 Qurius AS 45 Aladdin 49 SIRA Holding BV 59 Infodis 66 ianywhere Solutions/ a Sybase Company 66 Sybase ianywhere / Elmo ICT Systems 71 Furore 72 Adverteren? Informatie over adverteren en de advertentietarieven kunt u vinden op onder de rubriek Magazine. voorwoord Voor je ligt alweer het 3e nummer in de nieuwe vormgeving. Het lijkt erop alsof we inmiddels een behoorlijk evenwicht hebben gevonden, want dit keer is het vooral wat schaafwerk dat verricht wordt. We willen b.v. op de een of andere manier duidelijk maken welke pagina s bij eenzelfde artikel horen. Daartoe zie je op veel pagina s bovenaan het logootje van het betreffende network in het klein. En zo proberen we ook om advertenties niet dwars door een artikel heen te plaatsen (alleen de middenpagina s blijven daarin een uitzonderingspositie houden). Toch is er ook deze keer wel iets substantieels veranderd, en misschien had je dat in een oogwenk gezien of meende je het te voelen. Dit magazine telt nl. 72 pagina s i.p.v. de gebruikelijke 64; het is dus 12,5% dikker. 2 Zaken hebben daar aanleiding toe gegeven: het groeiende aantal networks en de toegenomen belangstelling om te adverteren. Wat het 1e betreft: ook deze keer wordt weer een nieuwe tak aan de SDN-boom voorgesteld, te weten Core Systems. Rolf Craenen is daar de trekker van en stelt het network en zichzelf voor op pag. 45. En in dit magazine ook al een 1e artikel vanuit het network van de hand van Michelle Cordes en Christiaan Heidema met als titel The Best of Both Worlds: Modernizing Core Sytems. Een onderwerp dat alle SDN-ers vast aan zal spreken, en wat ook meteen uiting geeft aan de wens van Rolf om niet een op zichzelf staand onderdeel van het SDN te worden, maar te streven naar zoveel mogelijk integratie en samenwerking met de bestaande networks. Om maar 2 voorbeelden te noemen: architectuur en databases spelen uiteraard ook in de wereld van Core Systems een prominente rol. De 2e aanleiding, het toenemen van de adverteerbereidheid bestaat dat woord? -, is iets wat we ons voor 1 oktober met groot gemak voor konden stellen. De behoefte aan personeel leek met de dag te groeien. Maar inmiddels hebben we de maand oktober beleefd, en sommigen met een grotere intensiteit dan ze lief was. Blijft echter tot dit moment een feit dat we meer advertenties kwijt konden dan we misschien wilden met het oog op de verhouding tussen informatie en advormatie. Om te voorkomen dat die verhouding scheef groeit hebben we in ieder geval deze editie uitgebreid tot 72 pagina s. En de volgende keer zullen we wel weer zien Die volgende keer is sowieso een bijzondere keer, want dat wordt uitgave nr. 100! Binnen de redactie wordt al druk gebrainstormd over de ideeën om daar iets bijzonders van te maken, maar als een van jullie daar een briljante ingeving voor heeft of b.v. nog heel bijzonder materiaal laat het ons weten! Voor jullie informatie: magazine nr. 1 dateert van 1 mei 1990, toen het SDN nog Clipper Developers Group Netherlands - CDGN dus heette. Het tijdschrift droeg de titel CONFE- RENCE met als ondertitel magazine voor de professionele clipper gebruiker. Enkele onderwerpen die in die 1e editie aan bod kwamen waren Netwerken, Netwerken en Clip- Tools, Memory Management, Bits, bytes en woorden en Bus Report. En dit laatste had niets met openbaar vervoer te maken Het grappige is dat in dit laatste pre-100 magazine enkele van die onderwerpen nog steeds actueel blijken. Alex Thissen b.v. heeft het over (het programmeren van applicaties voor) netwerken, alleen vertelt hij hoe je dat vandaag de dag kunt doen voor Peer-to-Peer netwerken met.net Framework 3.5. En Dennis Vroegop laat in zijn artikel C# Deep Dive zien dat het ook nu nog verstandig is - en als je je echt wilt onderscheiden van de mindere goden zelfs noodzakelijk is om kennis te hebben hoe de datastructuren binnen je applicatie omgaan met het beschikbare geheugen. Plaatsen we het op de stack of toch op de heap, en wat zijn de spelregels die we daarbij te hanteren hebben? Daarentegen hebben we natuurlijk ook volop nieuw materiaal, zeker in verhouding tot 18 jaar geleden, en gelukkig maar. We willen (ons) natuurlijk jong blijven (voelen). Peter van de Sman b.v. heeft de MP3-tags ontdekt en onderzocht, en doet daar verslag van in zijn artikel MP3 Tags in Delphi 2009 hij loopt dus zelfs al 19 jaar voor op nummer 1. En ook Marcel de Vries werpt zijn blik al over de grens van dit jaar met een vooruitblik naar de nieuwe Visual Studio versies, nu nog bekend onder de codename Rosario. M.a.w. oude onderwerpen in een nieuwe context, én nieuwe onderwerpen in een nieuwe context, maar in ieder geval boeiend en inspirerend om te lezen, zo denken wij! Veel leesplezier! rob willemsen, redactie@sdn.nl magazine voor software development 3

4 Inhoud 03 Voorwoord Rob Willemsen 04 Inhoudsopgave 05 Peer to Peer Applicaties met.net Framework 3.5 Alex Thissen 11 MP3 Tags in Delphi 2009 Peter van der Sman 17 Agenda 2008/ Visual Studio Team System "Rosario" Marcel de Vries 23 Zeven Windows Communication Foundation Best Practices Bert Loedeman en Steef-Jan Wiggers 30 C# Deep Dive Dennis Vroegop 34 Delphi 2009 Taaluitbreidingen: Generics en Anonymous Methods Bob Swart 42 Portaltechnologie: SharePoint of DotNetNuke? Nick Boumans SDN EVENT Data & Storage 12 december 2008 NBC, Nieuwegein 45 Core Systems Rolf Craenen 46 Interesting Things: Increasing Speed Sander Hoogendoorn 47 Silverlight Biedt Nieuwe Mogelijkheden voor SharePoint Donald Hessing 50 The Best of Both Worlds: Modernizing Core Sytems Michelle Cordes en Christiaan Heidema 54 Reflection als Inpakpapier Sander van de Velde 60 Een Eerste Introductie in F# Piet Amersfoort 65 ASP.NET onder de Motorkap: Controls Maken Michiel van Otegem 67 BizTalk Server: Architectuur Steef-Jan Wiggers

5 .NET C# Alex Thissen Peer to Peer Applicaties met.net Framework 3.5 Windows XP en Vista bieden ondersteuning voor peer-to-peer netwerken. Peer-to-peer netwerken zijn vooral bekend door de filesharing netwerken, zoals KaZaA en BitTorrent en de applicaties die van deze netwerken gebruik maken. Maar peer-to-peer netwerken en -applicaties bieden meer mogelijkheden dan alleen het delen van bestanden. Zo kun je andere resources delen, bijvoorbeeld de rekenkracht van je machine, of online collaboratie vereenvoudigen. Toch is peer-to-peer nog niet goed bekend bij de meeste.net ontwikkelaars. Daar komt verandering in: het.net Framework 3.5 heeft een managed API gekregen voor het bouwen van peer-to-peer en collaboratieve applicaties met behulp van de voorzieningen in het besturingsysteem. In dit eerste artikel over peer-to-peer applicaties kijken we naar peer-to-peer terminology, het PNRP protocol en de bijbehorende managed API plus een simpele toepassing met behulp van de WCF PeerChannel API. Peer-to-peer netwerken Peer-to-peer (P2P) netwerken zijn er in verschillende soorten en maten. Een P2P netwerk is een normaal IP-gebaseerd netwerk. In het netwerk komen peers voor, die ook wel nodes worden genoemd. De nodes kunnen allerlei soorten resources zijn, zoals een applicatie, een gebruiker, een computer of anderssoortige hardware (printer, mobiele telefoon of zelfs een regensensor met IP-adres). De peers in een P2P netwerk zijn met elkaar verbonden. De verzameling van peers die onderling verbonden zijn wordt een mesh of graph genoemd. Het is mogelijk dat een P2P netwerk meerdere meshes bevat, aangezien groepen peers niet met elkaar verbonden hoeven te zijn. In een volledig verbonden mesh hebben peers verbindingen met alle andere peers. Naarmate het aantal peers in een netwerk groter wordt is het steeds moeilijker om rechtstreekse verbindingen te behouden. Het is vaak voldoende om alle peers via-via met elkaar te verbinden. Dit zijn de zogenaamde partially connected meshes. Fig. 1: Verschillende soorten meshes Om met alle ontwikkelaars te kunnen communiceren hoef je niet rechtstreeks met iedereen te praten. Het is voldoende om je boodschap aan een kleine groep kennissen te vertellen, die het doorvertellen aan hun kennissen, enz., totdat iedereen de boodschap heeft gehad. Op eenzelfde manier kun je ook met een enkeling communiceren, al is het direct doorgeven van boodschappen dan gerichter. Het aantal spillen, de topologie en de indeling van het netwerk is afhankelijk van de opzet van het P2P netwerk. Applicaties voor peer-to-peer netwerken De gemiddelde netwerk applicatie bestaat uit clients en servers. Denk aan een database applicatie en database server, of browser en webserver. De client en server hebben daarbij duidelijk verschillende rollen. De client werkt samen met de server, die altijd aanwezig is, een vast en bekend netwerk adres heeft en daarmee makkelijk te bereiken is. De clients communiceren onderling niet rechtstreeks, maar gebruiken de server als tussenpersoon, zoals bij een chat-applicatie en -server. Daardoor hoeven de clients elkaar niet te kennen of te kunnen bereiken. Een applicatie die gebruik maakt van een peer-to-peer netwerk gedraagt zich doorgaans anders. De applicatie meldt zich vanaf de host machine aan op dit netwerk en wordt daarmee een peer in een mesh. De machine heeft doorgaans een wisselend netwerkadres. Verder moeten de clients rechtstreeks met elkaar kunnen communiceren, liefst zonder tussenkomst van centrale servers. De doorsnee handelingen van een P2P applicatie zijn: Aanmelden op een P2P netwerk De applicatie meldt zich aan op een P2P netwerk en voegt zich bij een bestaande mesh van peers. De nieuwe peer zal één of meerdere verbindingen met peer nodes aangaan. Hierna maakt de applicatie echt deel uit van het mesh. Als er nog geen mesh is, zorgt de applicatie voor het aanmaken van het mesh. Communicatie met alle peers De applicatie kan via het mesh eenvoudig met alle peers tegelijk communiceren. De applicatie voert dan een broadcast uit over het mesh. De broadcast start met het aanspreken van de directe peers, die de boodschap weer doorspelen naar hun directe peers, enz. magazine voor software development 5

6 .NET C# Interactie met specifieke peers De applicatie kan ook samenwerken met bewust gekozen peers. Het is soms nl. wenselijk dat er directe communicatie plaatsvindt en niet via-via. De directe interactie is alleen mogelijk wanneer het netwerkadres van de gewenste peers achterhaald kan worden. Daarna kan de applicatie via gangbare mechanismen communicatie starten tussen de peers, bijvoorbeeld met behulp van sockets, ASMX web services of WCF services. De peers van een pure P2P applicatie zijn allemaal gelijkwaardig. De minder pure vormen van P2P applicaties kennen een soort van centralisatie, doordat er dedicated servers in het netwerk zijn die coördinatie en administratie van het netwerk verzorgen. Soms zijn er in het P2P netwerk peers met een speciale functie (ook wel super-peers genoemd) aanwezig. Dit betekent veelal dat er een bestaande infrastructuur nodig is om de P2P applicatie te kunnen laten functioneren. De pure P2P applicaties kunnen juist ad-hoc functioneren. Peer name resolution Microsoft heeft bij de implementatie van peer-to-peer netwerken gekozen voor het IPv6 protocol. Als je nog niet bekend bent met IPv6 kun je op meer informatie vinden door te zoeken op IPv6 technical reference. De reden om te kiezen voor IPv6 is dat de beschikbare IPv4 adressen (2^32 mogelijkheden) inmiddels schaars zijn, terwijl IPv6 (2^128 mogelijkheden) nog meer dan genoeg adressen beschikbaar heeft voor alle computers, devices en applicaties die er nog gemaakt gaan worden in de nabije toekomst. Dit betekent voor het P2P netwerk dat iedere peer zijn eigen unieke IPv6 adres zou kunnen krijgen. Beschikbare IPv4 adressen zijn schaars; met IPv6 kan alles en iedereen een adres krijgen De immense hoeveelheid IPv6 adressen en ook mogelijke peers in een P2P netwerk brengt diverse uitdagingen met zich mee voor het P2P netwerk en de applicaties. Ten eerste draait het grootste deel van de huidige netwerk-infrastructuur nog op IPv4, dat standaard gescheiden is van een IPv6 netwerk. Dit probleem heeft Microsoft opgelost met een UDP tunnelling protocol genaamd Teredo. Dit protocol zorgt voor de overgang van een IPv4 netwerk naar het IPv6 netwerk en eventueel nog terug. Zo kunnen resources met een IPv6 adres binnen een IPv4 netwerk communiceren met en via IPv6 netwerken. Verder zal het in een IPv6 gebaseerd netwerk moeilijk zijn om andere peers te vinden zonder gebruik te maken van centrale servers. Microsoft heeft hiervoor het Peer Name Resolution Protocol (PNRP) ontworpen en geïmplementeerd. PNRP is een gedistribueerd en (nagenoeg) serverloos protocol, gemaakt voor schaalbare en betrouwbare naam-publicatie en -resolutie. Het protocol maakt gebruik van PNRP identifiers. Een PNRP ID is een 256 bit unsigned integer, bestaande uit een 128 bit hash van een PNRP peer name (ook wel P2P ID) en een 128 bit gegenereerd nummer voor de service location, dat een indirecte verwijzing is naar de locatie van de peer. Het service location deel in het PNRP ID maakt deel uit van een numerieke namespace waarbij de getallen relatieve afstanden voorstellen tussen adressen van de peers. Hiermee kan tijdens een zoektocht naar een specifieke peer steeds dichter naar die peer genavigeerd worden. Fig. 2: PNRP (Bron: Microsoft TechNet ( library/bb726971(en-us).aspx)) Een voorbeeld van een PNRP ID is: 198e25a029dfaef373bd86adac7dd2ef.69b5db7be977e1870aa f5f5d6e Een PNRP publisher registreert zijn zelfgekozen peer naam en bijbehorende IPv6 eindpunten bij een mesh waar het deel van uit wil gaan maken. Een PNRP resolver kan op basis van de peer naam of het PNRP ID op zoek gaan naar een specifieke peer en daarvan de geregistreerde eindpunten achterhalen. Het elegante van het PNRP protocol is de gedistribueerde cache van PNRP IDs die wordt bijgehouden door alle geregistreerde peers. Iedere peer houdt een meerlaagse cache bij van geregistreerde IDs. Tijdens het resolven van een peer naam wordt er via de caches van peers gezocht naar de uiteindelijke locatie van de registrerende peer. Laten we dit nog eens vergelijken met het zoeken naar een specifieke.net ontwikkelaar. Stel je bent op zoek naar een zekere programmeur in Leeuwarden, terwijl je zelf in een ander deel van Nederland zit. Je kent alleen de naam van die programmeur en hebt geen adres. Je kunt dan je directe kennissen vragen of zij die persoon kennen. Zij kennen (in hun eigen adressenlijst c.q. cache) wel een aantal personen, maar waarschijnlijk niet die ene persoon. Als zij iemand kennen die meer in de buurt woont, bijvoorbeeld in Drenthe of Friesland, dan geven zij het adres van die persoon door. Vervolgens kun je aan die persoon vragen of deze de gezochte persoon kent. Zo kom je alsmaar dichterbij en vind je uiteindelijk hopelijk de juiste persoon. Een programmeur hoeft in dit scenario niet alle anderen te kennen. Het is voldoende om binnen je eigen straat, stad en je provincie een relatief klein aantal personen te kennen. Dit komt overeen met de meerlaagse cache van PNRP. Het maakt het PNRP protocol schaalbaar voor grote aantallen peers, zoals in de orde van tientallen of honderden miljoenen. Verder krijgt iedere PNRP peer een unieke DNS naam eindigend op pnrp.net, waarmee je via het netwerk overal te vinden en bereiken bent. Dit geldt zowel voor applicaties als andere resources die gebruik maken van PNRP. Zo kan je computer zich ook registreren en een DNS naam krijgen. Een dergelijke naam heet een Windows Internet Computer Name (WICN). PNRP onderscheidt een aantal clouds waarbinnen geregistreerde peers zich bevinden. Een cloud wordt gevormd door de peer nodes die zich daarin bevinden. PNRP heeft clouds op basis van het type IPv6 adres. Deze soorten clouds zijn: Global: een globale cloud voor alle peers met een globaal IPv6 adres, dat geldig is op het gehele Internet. Link-local: locale (intra-net) cloud met peers die een zelf-toegewezen IPv6 adres hebben. De term link-local slaat op lokale netwerk-link (link = interface card), oftewel een lokaal netwerk via een specifieke netwerkkaart. Je kent 6 MAGAZINE

7 .NET C# de link-local adressen waarschijnlijk wel van IPv4, die beginnen met *.*. Deze adressen worden door de netwerklaag toegewezen aan netwerkkaarten die geen DHCP server kunnen vinden om automatisch een IP adres te verkrijgen. Zo kunnen computers in een netwerk zonder DHCP toch met elkaar communiceren via het link-local netwerk. Eenzelfde principe wordt gehanteerd bij IPv6. De meeste infrastructuren hebben nog geen manier om lokale IPv6 adressen uit te delen. De netwerkkaarten krijgen daarom een linklocal IPv6 adres beginnend met fe80:: toegewezen via het Neighborhood Discovery process. De grote beperking van link-local adressen is het lokale karakter: routers kunnen link-local verkeer niet doorsturen buiten de link. Site-local: Deze cloud wordt niet meer gebruikt. De clouds zijn vooral relevant voor de scope van een geregistreerde peer naam. De peer namen zijn alleen geldig en vindbaar binnen een cloud. De link-local cloud is dus alleen interessant voor peer-to-peer applicaties die in een lokaal netwerk gebruikt gaan worden. Er zijn veel meer mogelijkheden in de global cloud, aangezien daarin peers uit het hele Internet voorkomen. Dit betekent dat peers niet op dezelfde lokale netwerk-link hoeven te zitten om met elkaar verbonden te zijn. Een applicatie of resource die zich wil aanmelden op de PNRP global cloud moet allereerst een IPv6 adres hebben. Mocht de infrastructuur IPv6 voorzieningen hebben, dan is dit eenvoudig geregeld. Echter, meestal is het interne netwerk dat verbonden is aan het globale Internet een IPv4 netwerk en niet in staat om IPv6 adressen toe te wijzen. Het eerder genoemde Teredo protocol kan naast tunnelen van IPv4 naar IPv6 ook IPv6 adressen uitdelen aan Teredo clients. Deze adressen zijn te herkennen aan de 2001::0000::/32 prefix. PNRP en het.net Framework 3.5 Het.NET 3.5 Framework heeft een assembly System.Net.dll met daarin de namespaces System.Net.PeerToPeer en System.Net.Peer- ToPeer.Collaboration. We zullen in eerste instantie kijken naar de eerste namespace. De System.Net.PeerToPeer namespace bevat allerhande typen voor het werken met PNRP. Zo kun je bijvoorbeeld alle beschikbare PNRP clouds op een systeem als volgt enumereren: CloudCollection clouds = Cloud.GetAvailableClouds(); foreach (Cloud cloud in clouds) Console.WriteLine("Cloud 0 with scope 1 (ID=2)", cloud.name, cloud.scope, cloud.scopeid); Ofschoon de eerder genoemde PNRP IDs een prominente rol binnen het protocol spelen, zal een applicatie zich alleen bemoeien met de peer naam. Een peer-to-peer applicatie die zichzelf vindbaar wil maken via PNRP zal zijn peer naam registreren. Een peer naam bestaat uit een autoriteit en classifier in de vorm authority.classifier. De classifier is een zelfgekozen en meestal makkelijk te lezen naam. De autoriteit wordt automatisch bepaald en geeft aan of de peer naam een secure of unsecure naam is. Unsecure peer namen geven geen garantie dat de naam authentiek is en zijn daarmee makkelijk te spoofen. De autoriteit is dan altijd 0. De secure namen daarentegen gebruiken een public/private key pair voor het berekenen van een hash voor de autoriteit. Het bezit van de private key bewijst dat de publisher daadwerkelijk eigenaar is van de peer naam. Dit zijn een tweetal voorbeelden van peer namen: 0.LX (unsecure) f11314d1d3afea14b5e2ec32bf59d8574.LX (secure) Registratie van peer namen Het registreren van een peer naam gaat als volgt: PeerName name = new PeerName("LX", PeerNameType.Unsecured); PeerNameRegistration registration = new PeerNameRegistration( name, 1337, Cloud.AllLinkLocal); registration.useautoendpointselection = false; IPAddress address = IPAddress.Parse("IPv4 or IPv6 address"); IPEndPoint endpoint = new IPEndPoint(address, 1337); registration.endpointcollection.add(endpoint); registration.comment = "GameStats version "; registration.data = Encoding.UTF8.GetBytes("LX game statistics:..."); registration.start(); Het PeerName object wordt geconstrueerd met de classifier LX en er is gekozen voor een niet secure peer naam. Vervolgens wordt de registratie uitgevoerd via de PeerNameRegistration klasse. Hierin wordt de peer naam meegegeven, een poort die gebruikt wordt voor de registratie en communicatie met de PNRP peers en de clouds waarin registratie moet plaatsvinden. Dit kan de global cloud of alle link-local clouds (zoals in het voorbeeld) zijn, alle beschikbare clouds of een specifieke selectie van beschikbare clouds. De registratie van een peer name moet ertoe leiden dat de registrerende resource gevonden kan worden op een eindpunt. Deze eindpunten zijn IPv4 of IPv6 adressen met een bijbehorende poort. De registratie heeft daarom een collectie van IPEndPoint objecten nodig die gekoppeld gaan worden aan de peer naam. Tenslotte is het nog mogelijk om commentaar en data te bundelen met de geregistreerde naam. Het commentaar is bedoeld als een korte tekst van maximaal 39 karakters. De data mag een 4 KB grote byte array zijn en kan daarmee bijvoorbeeld geserializeerde objecten bevatten. Beide stukken informatie zouden moeten helpen om de juiste peer naam te selecteren. De registratie wordt uitgevoerd bij de aanroep van de Start methode van het PeerRegistration object. De peer naam is daarna voor het betreffende AppDomain geregistreerd. Andere applicaties kunnen eventueel dezelfde peer naam registreren, maar blijven uniek vindbaar. Resolven van peer namen De volgende logische stap is het vinden van een specifieke peer met behulp van PNRP. Het zoeken naar een peer kan alleen vanuit een ander AppDomain dan diegene die heeft geregistreerd. De Peer NameResolver klasse probeert een peer name te vinden. De resolutie kan synchroon of asynchroon uitgevoerd worden. Het is aan te raden om de asynchrone variant te gebruiken, aangezien een peer name resolutie enige tijd in beslag kan nemen en de synchrone aanroep in die tijd blokkeert. Het onderstaande fragment toont hoe je asynchroon een peer name kunt proberen te achterhalen. PeerNameResolver resolver = new PeerNameResolver(); resolver.resolvecompleted += (sender, e) => foreach (PeerNameRecord record in e.peernamerecordcollection) Console.WriteLine("Peer 0 at address 1", record.peername, record.endpointcollection[0]); ; resolver.resolveprogresschanged += (sender,e) => Console.WriteLine(e.ProgressPercentage); magazine voor software development 7

8 .NET C# Console.Write("Enter unsecure peer name: "); string nametoresolve = Console.ReadLine(); PeerName name = new PeerName(nameToResolve, PeerNameType.Unsecured); resolver.resolveasync(name, nametoresolve); Console.WriteLine("Waiting Press ENTER to quit"); Console.ReadLine(); De twee lambda expressions zijn de delegate methoden die worden aangeroepen bij de events ResolveProgressChanged en Resolve- Completed. Deze methoden hebben EventArgs die de voortgang van de resolutie dan wel de gevonden records bij de gezochte peer naam aangeven. De resolutie start bij de aanroep van ResolveAsync. De applicatie zal daarna wachten tot de operatie is voltooid. Het blokkeren van de main thread hoeft niet persé. Een WinForms of WPF applicatie kan doorgaan met het afhandelen van UI events. Het wachten tot de resolutie klaar is kan ook gedaan worden met een multithreading primitieve, zoals het AutoResetEvent. Bouwen van P2P applicaties Na al deze informatie vraag je jezelf wellicht af hoe hiermee een P2P applicatie gebouwd kan worden. P2P applicaties kunnen PNRP inzetten om meshes van peers te vormen binnen een PNRP cloud. Dit kan door slim gebruik te maken van geregistreerde peer namen. De applicatie kan met de peer namen de adressen van andere peers achterhalen en zelf de connecties met de andere peers regelen volgens een gekozen algoritme. P2P applicaties met WCF PeerChannel Een deel van de Windows Communication Foundation stack helpt je om P2P applicaties te maken. De PeerChannel klassen werken met meshes van peers in een P2P netwerk. WCF regelt en onderhoudt de connecties tussen de peers in het mesh en zorgt ervoor dat het mesh intact blijft. WCF gebruikt standaard PNRP om peer namen te registreren en later te kunnen resolven. Alle applicaties die het PeerChannel gebruiken, registreren dezelfde applicatie-specifieke unsecure naam middels PNRP. Hierna kan WCF alle peers in het mesh eenvoudig achterhalen door deze ene naam te resolven. De beveiliging van het WCF mesh wordt niet met behulp van secure peer namen geregeld. Immers, iedere peer zou dan een naam met een verschillende autoriteit hebben, die niet bekend is bij de andere peers. In plaats daarvan beveiligt WCF de toegang tot het mesh met behulp van een wachtwoord of een certificaat. Dit gebeurt indirect, aangezien iedereen een peer naam kan registreren in het mesh en PNRP geen beveiliging kent. Een nieuwe peer in het mesh moet verbinding maken met een aantal nabij gelegen peers. Tijdens het opbouwen van de verbinding vindt authenticatie plaats middels het wachtwoord of het certificaat. De communicatie van de boodschappen tussen de peers is ook te beveiligen met encryptie op het transport of de boodschap. boodschap naar alle peers in het P2P mesh. De operatie is one-way, aangezien er een broadcast gedaan wordt over het mesh en er geen antwoorden verwacht worden. Dit is typisch voor alle operaties van PeerChannel. Verder zullen de peers gebruik maken van een duplex channel, omdat ze zowel boodschappen ontvangen en ook op eigen initiatief boodschappen gaan versturen. Een duplex channel moet een service contract hebben dat een callback contract heeft. Het callback contract is hetzelfde gehouden als het gewone contract, aangezien de rol van iedere peer symmetrisch is: verzenden en ontvangen. [ServiceContract (Namespace="urn:www-sdn-nl:services:p2p:chat", CallbackContract=typeof(IChat))] public interface IChat [OperationContract(Name = "Say", IsOneWay = true)] void Say(string text); De implementatie van de service is simpelweg het schrijven van een binnenkomend bericht naar de console. class ChatClient: IChat #region IChat Members public void Say(string text) Console.WriteLine("0: 1", DateTime.Now.ToLongTimeString(), text); #endregion Nu zal het nodig zijn om een proxy aan te maken, zodat de services met elkaar kunnen communiceren. Wellicht dat het nu enigszins vreemd lijkt te worden. Immers, in een puur P2P netwerk komen alleen maar clients voor, maar nu hebben we het toch over een service met een proxy (oftewel client). In het geval van PeerChannel moet je dit wat ruimer zien. De implementatie van de service is die van de peer client. De proxy is de geijkte WCF manier om met een service te communiceren, ook al is dat in P2P een andere instantie van dezelfde service implementatie. Bedenk dat de service is feite een peer is, oftewel een client in het P2P netwerk. Alle peers zijn allemaal servers op het moment van een broadcast naar het mesh. Zij ontvangen allemaal de binnenkomende client boodschap. In een puur P2P netwerk komen alleen maar clients voor PNRP stelt de nodige eisen aan de inrichting van het netwerk, zoals IPv6 en globale internet connectie. WCF kan daarom ook custom resolvers gebruiken voor het achterhalen van de peers in het mesh. De CustomPeerResolver Service is een custom resolver implementatie die wordt meegeleverd met WCF. Wanneer deze resolver service wordt gehost, kunnen PeerChannel gebaseerde applicaties de resolver gebruiken. De P2P applicatie is daardoor niet meer afhankelijk van PNRP, maar verliest zijn server-loze karakter. Voorbeeld applicatie met WCF Laten we eens kijken hoe met WCF een heel eenvoudige server-loze P2P chat applicatie gebouwd wordt. Allereerst definiëren we een interface met één enkele operatie voor het versturen van een chat Jammer genoeg heeft het PeerChannel nog geen ondersteuning voor tooling als svcutil.exe en wsdl.exe. Het is dus niet mogelijk om met die tools een WCF proxy te maken. De benodigde code is ook met de hand te schrijven. De volgende interface zorgt voor een client channel waarmee de DuplexChannelFactory een proxy kan instantiëren. public interface IChatChannel: IClientChannel, IChat We maken verder geen volledige proxy klasse, maar laten de WCF infrastructuur een dynamische proxy genereren. Hieronder staat de code van de console applicatie die de WCF chat service gaat hosten. 8 MAGAZINE

9 .NET C# class Program static IChatChannel channel; static void Main(string[] args) ChatClient client = new ChatClient(); InstanceContext context = new InstanceContext(client); DuplexChannelFactory<IChatChannel> factory = new DuplexChannelFactory<IChatChannel> (context, "ChatClientEndPoint"); factory.credentials.peer.meshpassword = "123"; channel = factory.createchannel(); PeerNode node = channel.getproperty<peernode>(); channel.open(); Console.WriteLine("Listening on port 0", node.port); Console.WriteLine("Online? 0", node.isonline); Het ABC van de client configuratie laat het volgende zien: Adres: Het scheme van het netwerkadres is net.p2p voor Peer- Channel. Het host-adres definieert niet zozeer een bestaand IPv4 of IPv6 adres, maar de naam van het mesh waar de peer deel van gaat uitmaken. Binding: De binding voor PeerChannel is de netpeertcpbinding. De bijbehorende binding-configuratie laat zien hoe in dit geval de beveiliging transport met wachtwoord is en dat de resolutie van peers met PNRP wordt gedaan. De poort die op 0 is ingesteld geeft aan dat iedere peer zelf een beschikbare poort mag kiezen om te luisteren naar inkomend verkeer. De implementatie van de console applicatie laat zien wat de poort daadwerkelijk is geworden. Contract: Het IChat interface vormt het service contract. Hieronder is de applicatie in werking te zien. Negeer het schoonheidsfoutje van de ontvangende clients die op de Say:> regel ook de ontvangen boodschappen tonen. while (true) Console.Write("Say:> "); string text = Console.ReadLine(); channel.say(text); Veel van de bovenstaande code is WCF specifiek. De dikgedrukte statements laten aspecten van het P2P programmeren met WCF zien. Allereerst valt te zien hoe het wachtwoord voor de peer authenticatie in het mesh opgegeven wordt. Verder kan de service de peer node opvragen en daarmee achterhalen wat de poort is waarop de service gaat luisteren naar binnenkomend verkeer en of de node online is. Dat laatste verdient enige uitleg. Een peer in een mesh wordt gezien als online wanneer er meer dan één peer aanwezig is. Indien er maar één peer in het mesh is noemt men deze offline, ook al is er op dat moment wel degelijk een netwerkconnectie. Tenslotte bevat de while loop de aanroep van de service-operatie. Deze ene aanroep via de proxy vanuit de peer zal ertoe leiden dat alle peers (inclusief de peer zelf) in het mesh deze aanroep krijgen. Een deel van de configuratie van de PeerChannel service staat in de app.config. Hierbij is het opvallend dat er alleen een <client> is geconfigureerd. Echter, in lijn met de eerdere opmerkingen, was dit te verwachten. <system.servicemodel> <behaviors /> <client> <endpoint name="chatclientendpoint" binding="netpeertcpbinding" bindingconfiguration="peertransportbinding" address="net.p2p://meshname:1337/client" contract="wcfpeerchanneldemo.ichat" /> </client> <bindings> <netpeertcpbinding> <binding name="peertransportbinding" port="0"> <security mode="transport"> <transport credentialtype="password" /> </security> <resolver mode="pnrp" /> </binding> </netpeertcpbinding> </bindings> </system.servicemodel> Fig. 3: Console applicatie running Merk op hoe de eerst gestarte chat client (bovenste in de figuur) aangeeft offline te zijn bij het opstarten. De andere clients geven aan online te zijn. Er zijn nog meer mogelijkheden met de PeerChannel API, zoals het luisteren naar statusveranderingen van een peer node; het gebruik van propagatiefilters die bepalen welke boodschappen doorgelaten worden; beperken van de broadcast door het limiteren van de hop-count (aantal nodes waarnaar gesprongen wordt); het schrijven van een eigen custom resolver. Het PeerChannel in WCF werd al in versie 3.0 van het.net Framework geïntroduceerd. De managed API voor PNRP zoals die in.net 3.5 zit, was toen nog niet beschikbaar. Vandaar dat WCF een eigen implementatie heeft. Nu de System.Net.PeerToPeer namespace beschikbaar is, kan iedere ontwikkelaar die een P2P applicatie wil maken en daarbij een mesh wil inzetten gebruik maken van PNRP om dat te realiseren. Samenvatting De eerste stap bij het ontwikkelen van P2P applicaties is het kiezen van een geschikt P2P netwerk. Microsoft biedt met IPv6 en het PNRP protocol een goede infrastructuur om op te bouwen. Een applicatieontwikkelaar die P2P features in wil bouwen in zijn applicatie zal dus in contact komen met nieuwe protocollen als IPv6, Teredo en vooral PNRP. De managed APIs van het.net Framework 3.5 bieden voorzieningen om met PNRP te werken en zo peers in een P2P netwerk te publiceren en te vinden en zodoende meshes te creëren. Tenslotte kun je met behulp van je favoriete communicatiemechanisme peers met elkaar laten samenwerken. Je grootste uitdaging voor het bouwen van een P2P applicatie is het bedenken van een goede toepassing. Alex Thissen is.net trainer en coach werkzaam bij Class-A in Woerden. magazine voor software development 9

10 Advertentie Bergler Nederland b.v.

11 DELPHI Peter van der Sman MP3 Tags in Delphi 2009 Je hebt zo van die momenten dat alles vanzelf op zijn plaats valt. Zo kocht ik afgelopen zomer een nieuwe MP3 speler. En die liet mij allerlei informatie zien die ik nog niet kende. Natuurlijk had ik bij het afspelen op de computer al wel eens gemerkt dat er soms wat merkwaardige titels voorbij kwamen, maar nu zag ik ineens bij bepaalde MP3 s ook plaatjes op mijn display verschijnen. Tijd dus om uit te zoeken hoe dat zit en hoe je die informatie kunt wijzigen, aanvullen of verwijderen. Bijna op dezelfde dag kreeg ik een testlicentie voor Delphi Dus waarom niet het nuttige met het aangename verenigen en met de nieuwe Delphi iets maken om mijn mp3 tjes op te schonen. levert de standaard voor al de toegevoegde informatie die officieel ID3 tags heten Gelukkig hoef je dat niet allemaal zelf te verzinnen. Naast mp3 s kun je op internet ook eenvoudig achterhalen hoe het allemaal in elkaar steekt. Een belangrijke bron is de website Die levert de standaard voor al de toegevoegde informatie die officieel ID3 tags heten. Het is ook een goed startpunt voor allerlei voorbeeldprogramma s, al dan niet met broncode. Maar deze broncode zal niet zomaar gaan werken onder Delphi En ze maken zeker geen gebruik van de nieuwe mogelijkheden die Delphi 2009 ons biedt. Dit artikel beschrijft een implementatie die zeker wel werkt onder Delphi Het MP3 format Voordat we aan de slag kunnen moeten we natuurlijk eerst weten hoe een MP3 bestand in elkaar steekt. De algemene opzet is eenvoudig: ID3v2 Tag (versie 2) Audio data ID3v1 Tag (versie 1). Zoals gezegd, heel eenvoudig. Voor de eigenlijke (audio) data kan informatie zijn geplaatst en erachter ook, waarbij opgemerkt moet worden dat dit optioneel is. Alleen audio-data of audio-data met maar één tag mag ook. En ja hoor, strikt genomen mag ook de audio-data ontbreken! En dat kan nog zinvol zijn ook: er bestaat een testset met tags waarmee het lezen van de (ID3v2) tags kan worden getest. En die testbestandjes hebben geen audio nodig! Wat we gaan doen is een TMP3file-class maken om van daaruit de verschillende onderdelen te benaderen. In eerste opzet ziet dat er dan als volgt uit: Type TMP3file = class V1tag : TID3v2Tag; Mpeg : TMpegData; V2tag : TID3v2Tag; Procedure LoadFromFile(aFilename:string); Procedure SaveToFile (afilename:string); Later zal ik daar nog op terugkomen. We beginnen met de drie verschillende onderdelen van het bestand, maar eerst... Een MP3Toolkit Meestal loop je bij een implementatie tegen allerlei (bij)zaken aan die opgelost moeten worden. Zo krijgen je bij het lezen van tags te maken met een aantal tabellen (bijvoorbeeld, genres, soorten plaatjes, toegestane talen) en wat specifieke rekenkunde. Om dat niet steeds ad-hoc op te hoeven lossen kies ik ervoor dat allemaal in een aparte unit te stoppen. En binnen die unit definieer ik dan ook (de meeste) types die gebruikt gaan worden. Bovendien kies ik ervoor de algemene functies en procedures in een aparte class te stoppen. Dat maakt het wat duidelijker wat allemaal bij elkaar hoort. Dus als ik het later heb over een tabel of functie uit de toolkit, weet u nu waar ik het over heb. Ik zal u de verdere beschrijving besparen, alle details kunt u vinden in de download. De ID3v1 Tag De ID3v1 tag is qua opzet eenvoudig. De definitie dateert van Het is een blok van 128 bytes dat achter aan het bestand wordt geplakt. De indeling ligt helemaal vast: type TID3v1Data = record ID : array [1..3] of AnsiChar; Title : Array [1..30] of AnsiChar; Artist : Array [1..30] of AnsiChar; Album : Array [1..30] of AnsiChar; Year : array [1..4] of AnsiChar; Comment: Array [1..30] of AnsiChar; Genre : Byte; Op het internet wemelt het van de voorbeeldprogramma s om de ID3v1 Tag te lezen (en te schrijven). En de meesten zullen niet meer correct werken onder Delphi Want het chartype is nu een 2-bytes type terwijl in de definitie toch echt het 1-byte type wordt bedoeld. Vandaar ook dat ik in de definitie specifiek het type AnsiChar gebruik (in alle voorgaande versies van Delphi was dit het standaard type). magazine voor software development 11

12 DELPHI Er zijn een paar kleine details: Genre. Het Genre is een byte. En die verwijst naar een standaardtabel met genres, die is opgenomen in de eerder genoemde toolkit. Tracknummer. Optioneel kan een tracknummer worden toegevoegd. Daar is eigenlijk geen plaats voor in de datastructuur, maar in dat geval worden de laatste twee bytes van het veld comment opgeofferd. Als Comment[29] een #0 is dan vinden we in Comment[30] het tracknummer (als byte). Het schermpje ziet er als volgt uit. Lezen en Schrijven Om de ID3v1 tag te lezen hoeven we dus alleen de laatste 128 bytes van het bestand te lezen. Als de header klopt, dat wil zeggen als het ID dat we lezen TAG is, dan gaan we ervanuit dat het inderdaad een versie 1 tag is. En als het niet klopt, dan is er dus geen versie 1 tag in het bestand aanwezig. Bij het schrijven van een ID3v1 tag kijken we eerst of er al een tag in het bestand is opgenomen. Zo ja, dan overschrijven we die; anders plakken we hem eenvoudig weg achter aan het bestand. Het is niet de bedoeling eindeloos tags achter het bestand te plakken. Karakterset of encoding Het mag duidelijk zijn dat de gedachte achter de ID3v1 tag is dat alles in ASCII karakters zou worden geschreven. Een meestal is dat ook zo (ik ben nog geen echte voorbeelden van het tegendeel tegengekomen). Een probleem is wel welke ASCII-tabel is gebruikt. De liefhebbers van de Nederpop zullen er niet zo snel tegenaan lopen, maar als je per ongeluk een liefhebber bent van Griekse muziek dan loop je toch een goede kans dat de tag is aangemaakt op een machine met een Griekse codepage. En als een Griek het over Mikis Theodorakis heeft, dan schrijft hij Μίκης Θεοδωράκης, wat wij dan weer te zien krijgen als Ìßêçò ÈåïäùñÜêçò. Op zich kunnen we dat nog wel regelen, we kunnen Windows opdragen met de Griekse codepage te gaan werken, maar als we ook nog liefhebber zijn van Russische muziek, wordt dat toch een moeizaam verhaal. Gelukkig kunnen we dat probleem nu afvangen als we tenminste weten in welke codepage de data is geschreven. In SysUtils vinden we het nieuwe TEncoding type. En een Encoding is niets meer of minder dan een verzameling procedures waarmee je een aantal stringmanipulaties kunt doen met als uitgangspunt de bijbehorende karakterset/codepage. De functie die wij nodig hebben is GetString. Deze functie verwacht als eerste een parameter van het type TBytes (=array of byte). Optioneel kunnen ook nog een startindex en een lengte worden opgegeven. Helaas kun je een array [1..30] of AnsiChar niet typecasten naar een TBytes, dus daar moeten we iets voor verzinnen. De code is als volgt: GRencoding:=TEncoding.GetEncoding(1253); SetLength(lBytes,30); system.move(id3v1data.artist[1], lbytes[0],asize); MyTekst:=GRencoding.GetString(lBytes,0,30) En zo krijgen we onze befaamde musicus weer te zien als een, in ieder geval voor een Griek, leesbare Griek. Het is overigens heel verleidelijk de laatste drie regels te vervangen door een enkele regel waarin de artist wordt getypecast naar een TBytes, dus: MyTekst:= GRencoding.GetString(TBytes(ID3v1Data.Artist),0,30) Maar zoals aangeven, het werkt echt niet. Het compileert uitstekend maar levert gegarandeerd een runtime-error op! In de implementatie heb ik op het scherm een comboboxje gezet waarin een aantal coderingen is opgenomen (zie verderop). De gekozen Encoding is de encoding die gebruikt wordt bij lezen en schrijven. Fig. 1: Voorbeeld voor een ID3v1 tag in de standaard tekstcodering Het vinkje geeft aan of er een Tag in het bestand is opgenomen (bij lezen). Bij het schrijven bepaalt het vinkje of de Tag al dan niet in het bestand wordt geschreven (eventueel wordt de tag dan verwijderd). Verder is het (deel)scherm een rechttoe rechtaan weergave van de datastructuur. In dit voorbeeld zien we tamelijk onleesbare teksten verschijnen. Dat komt omdat ik, bij wijze van voorbeeld, teksten in afwijkende coderingen heb geschreven. En om het nog erger te maken: ik heb zelfs voor ieder veld een verschillende codering gebruikt, een situatie die je in het echt niet tegen zult komen. Maar hier is het zinvol want het toont wat er zoal gebeurt. De teksten kunnen weer leesbaar gemaakt worden door de juiste codering te selecteren. Na voor Grieks te hebben gekozen ziet het scherm er als volgt uit: Fig. 2: Voorbeeld voor een ID3v1 tag met de Griekse tekstcodering Mikis Theodorakis is nu weer leesbaar (als je tenminste Grieks kunt lezen). De overige teksten worden behandeld alsof ze met de Griekse tekstcodering zijn geschreven. Het lijkt voor u misschien op Grieks, maar dat is het niet. Een Griek zou de titel lezen als Pxrrth[], Axlgipthp[], Aelipsro en dat lijkt ook voor hem nergens op. Om die teksten te kunnen lezen moeten we overschakelen naar de andere coderingen. Later zullen we nog zien welke. Over Encodings Vroeg of laat loop je tegen de vraag aan welke encodings er eigenlijk allemaal bestaan. In het generieke TEncoding-type worden enkele encodings gedefinieerd. Dit zijn class variabelen. Dat wil zeggen dat als je deze gebruikt ze maar één keer aangemaakt worden. Andere karaktersets moet je expliciet zelf aanmaken met de functie TEncoding.GetEncoding(Codepage:integer); De meest voorkomende codepages zijn de nummers 1250 (ANSII Europees) tot 1258 (ANSII Vietnamees). Die kunnen we dus eenvoudig 12 MAGAZINE

13 DELPHI aanmaken. Maar er zijn er nog veel meer (op mijn systeem zijn het er 53). Om die allemaal te kunnen vinden kun je de procedure Enum SystemCodePages gebruiken. Die doet precies wat de naam belooft. Je vindt hem in de Windows Unit. In plaats van cp_supported kun je ook cp_installed gebruiken, bij mij maakt het geen verschil. De CallBackProc ziet er als volgt uit: function CodePagesProc(aCodePageString:PChar): Cardinal; stdcall; var lstr:string; lcodepage:integer; begin lstr:=strpas(acodepagestring); lcodepage:=strtoint(lstr); // DOE ER IETS MEE Dan is er nog een klein detail. Met GetEncoding krijgen we wel de Encoding die hoort bij de betreffende CodePage, maar welke het is weten we, afgezien van het nummer, niet. Gelukkig kunnen we daar wel achterkomen door dit netjes aan Windows te vragen: Function GetCodePageName(aCodePage:integer):String; Var lpcpinfoex: CPINFOEXW; begin if GetCPInfoExW(lCodePage,0,lpCPInfoEx) then result:= lpcpinfoex.codepagename; De CPINFOEXW structuur bevat ook andere informatie, bijvoorbeeld het MaxCharSize. De TEncoding gebruikt dit intern om het veld IsSingleByte te vullen. Helaas gebruikt Codegear deze informatie niet om de Encoding meteen maar een zinvolle caption te geven. De MPEG data Het is niet de bedoeling van dit artikel om te beschrijven hoe je zelf de audio-data kunt manipuleren. Maar op internet vond ik wel de informatie [2] hoe je de basisinformatie weer kunt geven. Deze code heb ik integraal in het demoprogramma opgenomen. Op het scherm zien we na het lezen van een MP3-bestand de gegevens over bitrate, etc. Hiermee kan, praktisch gezien, onbeperkt informatie toegevoegd worden aan het bestand. De ID3v2 tag wordt aan het begin van het bestand geschreven. Om te weten of er een ID3v2 tag aanwezig is lezen we de eerste 10 bytes van het bestand. Als dat een geldige ID3v2-header is dan kunnen we de header verder lezen; zo niet, dan nemen we aan dat er geen ID3v2 header aanwezig is. De algemene structuur van de ID3v2 tag is als volgt: Header Extended Header frame 1.. frame n Footer Padding We zien dat de tag vooral bestaat uit één of meer frames. Afhankelijk van de versie (er bestaan drie versies van ID3v2) kan er ook nog Extended Header informatie en/of een Footer zijn opgenomen. De gedachte achter Padding is de volgende: omdat de ID3v2 tag aan het begin van het bestand is geplaatst moet in principe bij iedere kleine wijziging het hele bestand opgeschoven worden En dan is er nog Padding. De gedachte achter Padding is de volgende: omdat de ID3v2 tag aan het begin van het bestand is geplaatst moet in principe bij iedere kleine wijziging het hele bestand opgeschoven worden. Om dat te voorkomen kan/mag aan het einde van de ID3v2 tag een aantal nullen worden geschreven. Intermezzo De eerste versie ID3v2 is versie 2.00 en dateert uit Deze is in 1999 vervangen door id3v In 2000 is deze definitie in id3v2.4.0 aangepast. Het zijn echter maar relatief kleine wijzigingen die vooral betrekking hebben op de header en footer. In de praktijk blijkt vooral versie 2.3 gebruikt te worden. Bij mij thuis heb ik maar een paar bestanden met 2.4 informatie gevonden. Maar dat kan natuurlijk ook gewoon komen omdat de meeste van die bestanden afkomstig zijn van mijn eigen CD s en LP s en aangemaakt zijn met hetzelfde programma Het TID3v2Tag type Aangezien de versie2 tag feitelijk een lijst met frames is, kunnen we de TList goed gebruiken als basis. Of beter natuurlijk de TObjectList. Dan hoeven we ons geen zorgen meer te maken over het weer vrijgeven van de frames na gebruik. Standaard gebruik ik een afgeleide van TObjectList met twee extra functies: Fig. 3: De Mpeg gegegens uit het voorbeeldbestand De ID3v2 tag Het hoeft geen betoog dat de informatie die in de ID3v1 tag gestopt kan worden beperkt is. Daarom is in 1998 ID3v2 tag gedefinieerd. Type TMyObjectList=class(TObjectList) Protected Function d CreateItem:pointer; virtual; Public Function AddNewItem:pointer; End; De functie CreateItem is virtual zodat deze eenvoudig in afgeleiden aangepast kan worden. Overigens gebruik ik zelden private propermagazine voor software development 13

14 DELPHI d ties of procedures. Niets zo frustrerend als gedwongen worden hele lappen codes te dupliceren om ergens een detail te kunnen wijzigen. De ID3v2 Header Gelukkig heeft de header van de ID3v2 tag voor alle versies dezelfde opbouw: TID3v2Header = record ID: array[1..3] of AnsiChar; Version: Byte; Revision: Byte; Flags: Byte; TagSize: TInt28; Ook hier moeten we het ID weer specifiek als AnsiChar definiëren. Version en Revision zijn wat ze lijken te zijn. In de Flags vinden we informatie over het wel of niet aanwezig zijn van een Extended header en/of een Footer, wat dan weer wel afhankelijk is van de versie. De Tagsize geeft ons de totale omvang van de tag (inclusief de header). Dat is een safe sync integer. Ik geef het toe, ik had er ook nog nooit van gehoord, maar het blijkt een integer te zijn waarbij steeds bit 8 wordt overgeslagen. Om daar weer een gewoon getal van te maken moeten we wat met bits gaan schuiven. In de MP3toolkit is daar een functie Int28ToInt32 voor opgenomen. Volgens de definitie mag de informatie uit de extended header desgewenst geskipt worden In het voorbeeldprogramma heb ik een functie gemaakt om de header te decoderen. Deze functie geeft true terug als er een header wordt aangetroffen en false als dat niet het geval is. Als er een header is, dan wordt de position in de stream aan het begin van de lijst met frames gezet zodat we direct kunnen gaan lezen. Bij het interpreteren van de header wordt de eventuele Extended Header ingelezen en apart gezet. Deze kan dan eventueel later weer ongewijzigd worden geschreven. Het demoprogramma doet verder niets met deze informatie. Dat is volledig legaal: volgens de definitie mag de informatie uit de extended header desgewenst geskipt worden. ID3v2 frame Alle frames bestaan uit een frame-header gevolgd door een aantal bytes content. Op basis van de content kunnen de frames grofweg in 4 groepen worden verdeeld: Teksten: bijvoorbeeld de artiest of de titel van het nummer; Commentaar/Lyrics: dat zijn natuurlijk ook teksten, maar het bijzondere is dat deze teksten een taalkenmerk hebben; Plaatjes: bijvoorbeeld een afbeelding van de plaathoes; Overige: een bonte collectie verschillende frames. Bij het opzetten van een Frame-class moeten we dus in ieder geval rekening houden met de mogelijkheid van verschillende eigenschappen en/of subclasses. De eerste opzet van onze FrameClass zou er nu zo uit kunnen zien: Type TV2Frame=class protected d FHeader : TV2Header; FContent: TBytes; End; Maar dat werkt niet echt lekker : de header van versie 2.0 (6 bytes) ziet er anders uit dan de header van versie 2.3/2.4 (10 bytes). Het volgende tabelletje geeft de opbouw van de frame-header in de verschillende versies Versie 2.0 versie 2.3/2.4 ID 3*AnsiChar 4*AnsiChar Size 3 bytes 4 bytes StatusFlag - 1 byte FormatFlag - 1 byte Daarom kies ik ervoor de gegevens uit de frame-header bij het inlezen te vertalen naar de volgende properties. Het FrameID Een niet te negeren probleem is het verschil in ID. Een frame dat, als voorbeeld, de songtekst bevat wordt in versie 2.0 gedefinieerd als TT2 en in 2.3/2.4 als TIT2. Om dat op te lossen heb ik in de toolkit een tabel opgenomen waarmee de conversie van versie 2.00 naar 2.3/2.4 kan worden gemaakt. Andersom zou ook kunnen maar lijkt niet zo zinvol. In die tabel is tevens informatie opgenomen over het type frame. Voor een deel blijkt dat overigens al uit de naamgeving. Zo zijn ID s die beginnen met een T per definitie een TextFrame. Nou ja, natuurlijk is er een uitzondering, het frame TXXX is een User defined text information frame. Overigens mag een frameid alleen uit HOOFDletters en cijfers bestaan. Bij het inlezen wordt hierop getest. Als er een ongeldig FrameID wordt aangetroffen dan wordt aangenomen dat er een (lees)fout is gemaakt en wordt het lezen afgebroken. De Frame-Size De frame-size die we in de header vinden is in versie 2.3 en 2.4 bijna een gewone integer. Alleen staan de bytes niet in de volgorde die we gewend zijn. Die moeten we bij het inlezen even omdraaien. Hetzelfde is het geval bij versie 2.0 alleen is daar nog een extra complicatie omdat we maar 3 van de 4 bytes geleverd krijgen, de vierde moeten we er zelf even aanplakken. In de MP3toolkit is daar een kleine functie voor opgenomen. De Frame-vlaggen In een versie 2.3/2.4 header vinden we ook nog 2 vlaggen. De Statusvlag geeft informatie over de status van het frame. Zo is er bijvoorbeeld een vlag om aan te geven of een frame wel of niet gewijzigd mag worden (read only). Maar ook een vlag die aangeeft wat er met het frame moet gebeuren als de data wijzigt. Dat klinkt wat curieus maar het volgende voorbeeld maakt het wellicht begrijpelijker: in sommige bestanden kom je een PRIV, een private frame, tegen waarin het peaklevel is opgenomen. Een player kan die informatie gebruiken om het geluidsvolume te corrigeren. Dat frame is dus niet meer zinvol als we aan de audio gaan sleutelen. En dan is het natuurlijk handig als we dat aan een vlag kunnen zien. Maar op dit moment zijn we niet in de audio zelf geïnteresseerd dus negeren we de statusvlag gewoon. De Formatvlag is een ander geval. Hier vinden we onder andere informatie of er gebruik is gemaakt van encryptie en of compressie. Dat kan dus ook nog. Gelukkig ben ik het in de praktijk nog niet tegengekomen dus die formatvlag laat ik voorlopig ook met rust. Voor wie er in geïnteresseerd is, of het echt nodig heeft, kan de verdere documentatie over deze flags vinden op ID3v2 Textframes De meest gebruikte frames zijn TextFrames. Zo bevat het frame TPE1 de naam van de artiest, TIT2 de songtitel, TALB de naam van het album, en TRCK het tracknummer. Zoals eerder aangegeven zijn (bijna) alle frames die beginnen met een T per definitie textframes. 14 MAGAZINE

15 DELPHI En ook de frames die beginnen met een W zijn textframes, met dien verstande dat het de bedoeling is dat hier een webadres in staat. Zo is bijvoorbeeld WPUB de Publishers official webpage. En ook hier geldt weer een uitzondering voor WXXX. Bij het lezen (of beter gezegd: bij het schrijven) van textframes mogen er officieel maar 4 tekstcoderingen worden gebruikt: 0: Iso : UNICODE WITH BOM 2: UTF-16, without BOM 3: UTF-8 En voor wie echt oplet en denkt dat de twee codering op basis van de BOM (Byte Order Mask) eigenlijk in twee coderingen uiteenvalt heeft gelijk. Alleen is één van die twee coderingen dan toch weer gelijk aan UTF-16 dus houden we er toch weer 4 over. En ook hier kunnen we voor het lezen weer goed gebruik maken van het TEncoding type. Aangezien we met maar, maximaal, 4 coderingen te maken hebben genereren we die Encodings vast maar in onze framelist. Natuurlijk hebben we hier ook weer te maken met de eerder genoemde complicatie bij de ASCII codering. Officieel moet er in Iso8859 geschreven worden. Maar zoals eerder aangegeven is de praktijk dat iedereen schrijft volgens zijn eigen codepage. Dus komt ook hier onze beroemde Griek weer misvormd te voorschijn. En ook hier kunnen we dat eenvoudig oplossen door bij het lezen de standaard Iso8859 te overrulen door de op dat moment zinvolle Encoding. Eerder hebben we gezien dat we voor het converteren van data naar een string de volgende procedure gebruiken: MijnLabel := aencoding.getstring(fdata,astartpos,asize) Over de Encoding hebben we het net gehad, de data hebben we gelezen, maar nu moet het begin en einde van de tekst nog worden bepaald. Voor een Textframe is dat eenvoudig: het eerste byte geeft de codering aan en je begint bij het tweede byte (met index=1). Wat betreft de lengte: dat is een kwestie van zoeken. Natuurlijk mogen we nooit verder lezen dan de data groot is, maar we moeten ook ophouden als we een nul karakter tegenkomen. En dat is nog even oppassen, want bij de coderingen 1 en 2 betekent dat dus totdat je twee nullen achter elkaar tegenkomt! Ik heb daar maar een tweetrapsraket van gemaakt. In de eerste trap wordt de lengte van de string bepaald, waarna in de tweede trap de string wordt opgehaald. En dat is alles wat we nodig hebben om textframes te lezen! Picture Frames In de ID3v2 tag kunnen ook frames met afbeeldingen worden opgenomen. Een voor de hand liggend voorbeeld is natuurlijk de afbeelding van de (plaat)hoes. De opbouw van een pictureframe is als volgt: Mime Picturetype Description Data in versie 2.0 drie ascii karakters, in 2.3/2.4 een zero-terminated ascii string byte zero-terminated ascii string het feitelijke plaatje Er zijn 21 voorgedefinieerde afbeeldingstypes, wat weer een functie voor onze toolkit oplevert. De Mime (Multipurpose Internet Mail Extensions) geeft het type afbeelding weer. Meestal hebben we te maken met JPG (versie 2.0) en image/jpeg (versie 2.3/2.4). In het description veld zou een omschrijving kunnen staan, maar eerlijk gezegd heb ik nog nooit een voorbeeld gezien waar iemand de moeite heeft genomen daar wat in te zetten. Wat we nu bij het inlezen doen is deze gegevens decoderen en klaarzetten voor gebruik. Daartoe voegen we aan het TFrametype wat extra eigenschappen en functies toe. Een overweging zou zijn hiervoor een afgeleide TPicFrameType voor te maken. Maar daar heb ik vooralsnog van afgezien, en dit werkt prima! Het verwerken van een PictureFrame gaat als volgt: procedure TID3v2Frame.PreparePictureFrame (aversion :byte); var lstartpos:tsizetype; begin if FData<>NIL then begin case aversion of 2 : begin FMime := AsciiEncoding.GetString(FData,1,3); lstartpos:=4; else begin lstartpos:=1; FMime := AnyTextFromData(lStartPos, AsciiEncoding); self.fpictype := FData[lStartPos]; inc(lstartpos); self.fdescription := AnyTextFromData(lStartPos, AsciiEncoding); FDataStart:=lStartPos; Om de data van het plaatje op te halen maak gebruik ik van een TStream. Die kan dan direct aan een geschikte TGraphic gegeven worden om op scherm te tonen. Je moet dan overigens wel even aan de hand van de Mime controleren of het wel kan/mag! In het programma gebruik ik een TMemoryStream, maar je kan natuurlijk ook een TFileStream gebruiken om het plaatje op te slaan in een bestand. procedure TID3v2Frame.GetPicture(aStream: TStream); begin if FFrameType=v2fPIC then begin astream.writebuffer(fdata[fstartpicidx], FDataSize - FStartPicIDX ); Voor het schrijven gebruik ik ook weer een TStream. Je wordt geacht zelf het juiste mime-type mee te geven zodat daar geen problemen over kunnen ontstaan. procedure TID3v2Frame.SetPicture( astream: TStream; amime:string); begin setlength(fdata, astream.size); astream.seek(0, sofrombeginning); FDataSize :=astream.size; setlength(fdata,fdatasize); astream.read(fdata[0], FDataSize); FDataStart:=0; SetFrameIDStr(IDv2_PICTURE); FMime := amime; Bij het schrijven overschrijf ik de oorspronkelijke data. Die inhoud had ik toch al bij het inlezen verwerkt! magazine voor software development 15

16 DELPHI Commentaar frames Nog een speciaal geval. Er kunnen commentaarframes worden opgenomen. Deze hebben de volgende structuur: Tekstcodering Taal Omschrijving Commentaar 1 byte 3 bytes ascii zero terminated string string, (al dan niet zero terminated) De taal mag iedere taal zijn, zolang die maar voorkomt in de standaardlijst met talen. Dat is dus weer een standaardfunctie in onze toolkit! Omschrijving en commentaar dienen te zijn geschreven in de aangegeven tekstcodering. En opgepast, bij type 1 wordt er twee keer een BOM (Byte Order Mask) geschreven! Het commentaar kan/mag uit meerdere regels bestaan. Overige frames En dan is er nog een bonte verzameling overige frames. Hiervoor verwijs ik graag naar de site [1]. Sommige zijn user defined, daar kan je dus niet heel veel mee. Voor een aantal anderen geldt dat er een specificatieveld is gedefinieerd. Voor een aantal frames heb ik dat in de broncode verwerkt. Een Schermvoorbeeld Voor het weergeven van de ID3v2 gegevens is uiteraard meer nodig dan voor de ID3v1 gegevens. Toch geef ik de basisgegevens op identieke wijze weer. Dit zijn toch degene die je het meeste gebruikt en dan kunnen ze maar overzichtelijk klaarstaan. Voor het voorbeeldbestand ziet het scherm er als volgt uit: Rechts zien we het FrameID, met de omschrijving opgehaald in de MP3toolkit, links een deel van de inhoud. Merk op dat ook de standaardgegevens hier weer terugkomen. Die komen immers ook uit frames! De inhoud in de linkerkolom is in principe maar een gedeelte van de totale informatie in het frame. De rest van de informatie wordt ernaast getoond in een standaard TImage en TMemo component. Het schrijven de ID3v2 tag Om een lang verhaal kort te houden: dat is dus gewoon een kwestie van alle frames netjes achter elkaar plakken. Er zijn wel een paar dingen waar we rekening mee moeten houden Als er helemaal geen informatie is dan worden we geacht geen ID3v2 tag te schrijven (een lege tag mag dus niet) en voor de meeste frames geldt dat er geen dubbelen voor mogen komen. Dus geen twee titels, maar ook geen twee uitvoerenden! De eerste controle is ingebouwd, de andere laat ik graag over aan een ieders fantasie (het probleem is dat je dat allemaal moet terugkoppelen naar de gebruiker, opties aanbieden wat te doen met het dubbele frame, enz.). Het zal dus niet meevallen om een echt waterdicht programma te schrijven. Standaard schrijf ik in versie ID3v2.3 (die komt immers het meest voor). Bovendien negeer ik bij het schrijven eventuele extended data (al is het maar dat ik dan niet het probleem heb dat de extended data mogelijk uit een andere versie komt). En volgens de definitie mag ik deze gegevens ook negeren! Verder kan ik bij het schrijven opgeven welke padding ik maximaal wil gebruiken. En niet te vergeten in welke codering er moet worden geschreven. Het standaard kiezen van een Unicode codering heeft als nadeel dat de meeste teksten twee maal zo groot worden zonder dat dit echt zinvol is. Dus misschien zou UTF-8 een goede optie kunnen zijn. Ik heb een iets flexibelere oplossing gekozen: je kan in het voorbeeldprogramma kiezen voor één van de volgende mogelijkheden: altijd UTF8 altijd Unicode Ascii waar mogelijk, anders UTF8 Ascii waar mogelijk, anders UNICODE Fig. 4: De basisgegevens in de ID3v2 tag We zien nu alle talen gewoon door elkaar heen. Ik heb ook maar even aangegeven in welke codepage de gegevens in de ID3v1 tag zijn geschreven. (Een detail: De velden Album en Commentaar zijn geschreven in het arabisch en hebreews. Dus eigenlijk zou deze informatie rechts uitgelijnd moeten worden.) Onder deze standaardgegevens heb ik een tabel met alle gevonden frames opgenomen. Hieronder als voorbeeld een deel van de gegevens: Fig. 5: Alle frames uit de ID3v2 tag Bij de laatste twee opties wordt eerst gekeken of de tekst in de (standaard) ASCII codering goed wordt weergegeven. Zo ja, dan gebruiken we ASCII, zo niet de aangegeven multi-byte codering. Om te bepalen of er in ASCII kan worden geschreven kunnen we (alweer) gebruik maken van de TEncoding procedures. De truc is eigenlijk heel simpel: laat de encoding de string naar een TBytes vertalen, en zet die TBytes dan meteen weer terug naar een string. Als die hetzelfde is als waar we mee zijn begonnen dan kunnen we de Encoding gebuiken zonder dataverlies, anders schakelen we over naar de opgegeven multi-byte encoding: lbytes:=myasciiencoding.getbytes(avalue); if MyAsciiEncoding.GetString(lBytes)<>aValue then GebruikMultiByte; In de broncode is dit geïmplementeerd in de procedure GetAndSet- TextType. Deze bepaalt eerst de te gebruiken codering en zet vervolgens de juiste code in de outputdata. Zoals we gezien hebben bij het lezen is dat het eerste byte van de data. Het schrijven van de tags in een MP3-bestand Eerder heb ik aangegeven dat ik een TMP3file class gebruik waarin de tags als properties zijn opgenomen. Bij het schrijven wordt gekeken naar de EXISTS property van de beide tags om te bepalen of het geschreven moet worden. Als die op false staat, dan wordt een eventueel aanwezige tag automatisch verwijderd. Het vinkje op het scherm bepaalt hoe de property wordt gezet. 16 MAGAZINE

17 DELPHI Bij het schrijven van ID3v2 tag, die vooraan het bestand geplaatst moet worden, kunnen er twee situaties ontstaan: De nieuwe tag past precies in de ruimte van de bestaande tag De nieuwe tag is groter of kleiner dan de bestaande tag. In het eerste geval kunnen we de nieuwe tag eenvoudigweg over de bestaande tag heen schrijven. In het tweede geval moeten we de rest van het bestand verschuiven, naar voren of naar achteren. Om dat te voorkomen kan een padding worden toegestaan. Dat wil zeggen een aantal zero bytes aan het einde van de tag. In de TMP3file heb ik een property MaxPadding opgenomen. Deze bepaalt de speelruimte waarmee geprobeerd kan worden om van situatie 2 toch een situatie 1 te maken. Standaard heb ik die op een waarde van 512 gezet. Je moet tenslotte iets kiezen. Maar goed, op een gegeven moment ontkom je er niet aan. In dat geval maak ik een tijdelijk bestand aan, schrijf daar de nieuwe ID3v2 tag in, en plak er de audio-data en eventueel de ID3v1-tag achter. En vervolgens kopieer ik dat bestand over het oude bestand heen. Ik gebruik daar een TMP3tempfile type voor dat zelf een unieke naam verzint in de standaard TEMP dir en die dat tijdelijke bestand ook weer voor me opruimt als ik klaar ben. Het aanmaken van dat tijdelijke bestand gaat dus wel lukken. Maar wat blijkt nu: overschrijven op een CD lukt niet, zelfs niet met beheersrechten! Het programma test er nog niet op, een verbeterpuntje dus! En dan even nog dit. Een klein voorbeeldprogramma zegt meestal meer dan vele bladzijden tekst. Alle genoemde code, en meer, is terug te vinden in het bijbehorende zip-bestand, te downloaden van de SDN-site. Het staat een ieder vrij om die code te gebruiken en/of naar eigen inzicht aan te passen. Bij verdere verspreiding wordt bronvermelding op prijs gesteld. Uiteraard worden tips, aanvullingen, foutmeldingen enzovoorts ook op prijs gesteld. Maar voor u begint. Het gebruik van het voorbeeldprogramma is as is. Aansprakelijkheid voor het verloren gaan van uw dierbare muziekbestanden kan niet worden aanvaard. Aangeraden wordt altijd eerst een backup van uw bestanden te maken voordat u ze aan gaat passen (met dit voorbeeld programma of met ieder ander programma). Met name als de audiodata moet worden verplaatst kunnen er allerlei buffer problemen ontstaan, zeker in een netwerksituatie (vraag maar liever niet hoe ik dat weet). Maar laat u niet te bang maken. Die problemen zijn eerder incidenteel dan structureel. Het is alleen vervelend om je muziek kwijt te raken alleen omdat het netwerk even te druk is om zich met jouw bestandje bezig te houden. Daarnaast moet u er rekening mee houden dat iedere speler zijn eigen manier heeft hoe om te gaan met de informatie uit de tags. Gewoon een kwestie van proberen en zien wat er gebeurt. Zo accepteert mijn spelertje geen UTF8 teksten (het moet dan Unicode zijn). En daarnaast moeten de teksten ook nog met een nul afgesloten worden (wat volgens de standaard mag, maar niet hoeft). Het eerste kon ik nog wel uit de documentatie van de speler achterhalen, het laatste niet! En door het schrijven van dit artikel kwam ik er achter dat mijn speler wel Grieks en Russisch spreekt maar geen Arabische of Hebreeuwse teksten weer kan geven! Kortom het opschonen van je mp3 bestanden is maatwerk! Veel plezier! Links Bij het schrijven heb ik gebruik gemaakt van de volgende bronnen: [1] : op deze website is alle informatie over de ID3 tags terug te vinden. [2] het bestand MP3FileUtils.pas (v0.3a) van Daniel Gaussmann. Dit voorbeeld is te downloaden via Peter van der Sman Peter van der Sman begon op de middelbare school met programmeren, in een tijd dat dit nog met ponskaarten gebeurde. De introductie van de PC maakte hij mee als chemisch analist en later als statisticus. Twee werkvelden waarin de computer een nuttig hulpmiddel is om de grote hoeveelheden (meet)data te verwerken. Midden jaren 90 besloot hij, na zijn studie informatica, zich geheel te gaan wijden aan de automatisering. Via Prisman Consultancy houdt hij zich bezig met het maken van software en consultancy. AGENDA 2008/2009 SDN Event, NBC Nieuwegein...12 december SDN Magazine Nr februari VO DevFest, London (UK) maart SDN Event...27 maart SDN Magazine Nr mei Microsoft DevDays eind mei SDN Event...26 juni Genoemde data onder voorbehoud magazine voor software development 17

18 Advertentie Logica

19 .NET Marcel de Vries Visual Studio Team System "Rosario" Het zal de meeste ontwikkelaars niet zijn ontgaan: Visual Studio 2008 is gereleased. De Team System omgeving is in Visual Studio 2008 op een groot aantal punten verbeterd en je ziet dat het met deze omgeving steeds beter mogelijk wordt de softwarelifecycle onder controle te krijgen. Misschien ben je net aan de slag gegaan met Visual Studio 2008, of werk je nog gewoon met de Visual Studio 2005-omgeving, toch wil ik in dit artikel alvast een blik werpen op de toekomstige ontwikkelingen op het gebied van Visual Studio Team System (VSTS) en Team Foundation Server(TFS). Zelf ben ik van namelijk mening dat vooruitkijken erg belangrijk is voor het maken van de juiste keuzes vandaag. Door middel van dit artikel hoop ik je inzicht te geven in wat we kunnen verwachten van Microsoft als het gaat om het verder versterken van de tools die we nodig hebben om de steeds maar complexere applicaties goed te ondersteunen. De volgende versie van VSTS en TFS worden momenteel ontwikkeld onder de codenaam "Rosario". Voor "Rosario" heeft Microsoft een thema gedefinieerd waaraan alle nieuwe features worden opgehangen. Het thema is: "Bouw de juiste software op de juiste manier". Onder deze noemer zul je een aantal toevoegingen gaan zien aan de huidige toolset waarvan momenteel al een subset publiek beschikbaar is in de vorm van specificaties en een Community Technical Preview(CTP). In dit artikel baseer ik mij op de features die men al publiek heeft gemaakt en terug te vinden zijn in de april CTP. Bouw de juiste software op de juiste manier de diagrammen. Daarbij moet ik opmerken dat het Sequence Diagram er het meest compleet uitziet. Wat erg krachtig is aan de Sequence Diagrammen is dat men daarbij ook goed heeft nagedacht over het snel vol raken van een diagram waardoor je het overzicht dreigt te verliezen. Doordat je eenvoudig delen van de sequence kunt in- en uitklappen kun je heel snel overzicht krijgen. Tevens heeft men een optie toegevoegd om delen van een sequence te verplaatsen naar een ander diagram dat dan via een "click through" eenvoudig benaderbaar blijft. Op die manier is het prima mogelijk voor een architect zijn intenties duidelijk te maken, waarna de detailinvulling naderhand bijvoorbeeld wordt gecompleteerd door een ontwikkelaar. Er missen in de huidige implementatie van de diagrammen nog een aantal belangrijke features op zowel het UML-vlak als op het vlak van usabillity, maar het geeft al wel een aardig idee waar het uiteindelijk naartoe gaat. In de manier van gebruik van diagrammen binnen een project zijn er duidelijk twee stromingen te onderscheiden. De top down benadering waarbij de architect/ontwikkelaar begint met het maken van een ontwerp en de bottom up benadering waarbij er reeds sprake is van een bestaande codebase en men van daaruit diagrammen wil kunnen zien. Team Architect in Rosario ondersteunt UML 2.0 diagrammen Om die reden heeft Microsoft het ook mogelijk gemaakt vanuit de bestaande code diagrammen te genereren die een afspiegeling zijn van het gemaakte systeem. Hiervoor heeft men een zogenaamde Architecture Explorer gemaakt. Met deze Explorer kun je bij een bestaand product makkelijker inzicht verkrijgen in o.a. de structuur van het product. Je kunt er b.v. eenvoudig de eerder beschreven sequence- of class-diagrammen reverse engineeren. In figuur 1 is een screenshot te zien van de Architecture Explorer. Hierbij zie je in één oogopslag de explorer (onderkant) en het sequence diagram dat is gegenereerd op basis van de broncode in de Team Foundation Server. Visual Studio Team Architect Edition Het kan misschien als een schok komen, maar zelf ben ik zeer blij met het feit dat Microsoft er voor heeft gekozen in Team Architect UML 2.0 diagrammen te gaan ondersteunen. Daarbij moet wel direct worden opgemerkt dat het niet het doel is van Microsoft volledig UML te gaan ondersteunen. Men heeft de keuze gemaakt modellen te kunnen gebruiken als ondersteuning voor het bouwen van software en daarbij gebruik te maken van de kennis die men al heeft op dit gebied. Om die reden heeft men er ook voor gekozen alleen de meest gebruikte diagrammen te ondersteunen. De diagrammen die je in ieder geval terug zult vinden zijn: het (Conceptual) Class diagram, het (Conceptual) Sequence Diagram, het Component Diagram en het Use Case Model. In de april CTP kun je al een eerste indruk krijgen van een aantal van Fig. 1: Architecture Explorer en sequence diagram magazine voor software development 19

20 .NET Visual Studio Team Developer Edition Binnen de developer edition heeft men zich voornamelijk bezig gehouden met het probleem dat beter bekend is als het "Non Repro" probleem. Dit is het probleem waarbij een tester of eindgebruiker een fout constateert in het product maar dit vervolgens niet kan reproduceren in een ontwikkelomgeving. Het kost vervolgens zeer veel tijd en geld om het probleem boven tafel te krijgen. In sommige gevallen blijkt het zelfs echt onmogelijk om het probleem te reproduceren. Vaak is dit gerelateerd aan andere verschillen tussen productie- en ontwikkelomgeving en in steeds grotere mate gerelateerd aan concurrency problemen. Zeker met de komst van MultiCore-processoren zal dit probleem in de toekomst alleen maar groter worden. Om dit probleem het hoofd te gaan bieden introduceert Microsoft in de developer edition van Team System zogenaamde Historical Debugging. Historical debugging kun je het meest eenvoudig vergelijken met de Flight Data Recorder die in een vliegtuig zit. Deze recorder houdt de belangrijkste gegevens bij tijdens een vlucht en zodra er sprake is van een probleem (in het ergste geval zelfs een crash) dan zal alle benodigde data beschikbaar zijn voor analyse achteraf. Historical debugging doet feitelijk hetzelfde. Tijdens het testen van een applicatie kan men deze "flight recorder" op de achtergrond laten meedraaien. Gedurende het testen (of eventueel zelfs in productie) kan dan de tester bij het rapporteren van een bug de historische execution log aan het Bug-workitem koppelen. Met deze log kan vervolgens de ontwikkelaar direct zien welke codepaden zijn doorlopen. De integratie wordt zelfs zover doorgevoerd dat de debugger te switchen is tussen live mode en historical mode, waarbij in historical mode de ontwikkelaar door de code kan steppen alsof de applicatie live aan het werk is. Hierbij kan de ontwikkelaar alle bekende debug informatie zichtbaar maken, waaronder de waarden van variabelen die gebruikt zijn gedurende de uitvoering van het programma. Zaken als Exceptions, File IO, Registry Access, etc. worden ook allemaal vastgelegd. Je kunt je voorstellen dat dit natuurlijk een zeer grote vooruitgang is om het "No Repro" probleem het hoofd te bieden en dat je dan als ontwikkelaar niet eerst veel energie hoeft te steken in het reproduceren van het probleem voordat je het kunt opsporen. Je kunt dus bij het openen van een Bug direct de log laden, er door heen stappen en zien wat er mis is gegaan in de applicatie. Historical Debugging werkt als een Flight Recorder Historical debugging is al een aantal jaren in ontwikkeling bij Microsoft onder de Code Naam "Protheus". Op dit moment heeft men het in de planning zitten de Protheus runtime, die op de achtergrond de traces bijhoudt, als losse executable beschikbaar te stellen zodat je deze als onderdeel van je applicatie kunt meeleveren. Daarmee kun je dan zelfs in een applicatie een optie inbouwen om tijdelijk de Protheus logging aan te zetten. Dan kan zeer waardevol zijn indien de klant een lastig te reproduceren probleem constateert. Door dan de logs te laten opsturen kunnen die lastig te vinden productieproblemen eenvoudiger worden opgelost. Naast historical debuggen heeft men ook gekeken naar het concept van Impact Analysis. Daarbij heeft men zich momenteel beperkt tot Impact Analysis op het vlak van uit te voeren unittesten ten gevolge van het aanpassen van code. Hierbij gaat het erom dat tijdens het aanbrengen van aanpassingen in de code de omgeving kan bepalen wat de minimaal uit te voeren set unittesten is om te valideren of een aanpassing regressie tot gevolg heeft gehad. Het idee daarbij is dat men vaak enkele tientallen en soms zelfs ettelijke honderden unittesten beschikbaar heeft, maar geen idee heeft welke testen zouden kunnen aantonen of een aanpassing mogelijk regressie tot gevolg heeft gehad. Met behulp van Test Impact Analysis wordt op basis van de code-coverage-data uit de unit testen, die is opgeslagen op de server gedurende de builds, bepaald of een unittest uitgevoerd zou moeten worden. Zodra wordt geconstateerd dat een Unittest coverage heeft op de regels code die aangepast zijn, zal deze unit test als "aanbevolen" test worden aangemerkt. Je kunt dan vervolgens na afloop van het doorvoeren van je verandering deze aanbevolen lijst uitvoeren om te controleren of een van deze testen faalt. Deze feature is overigens niet alleen erg handig in de IDE, maar ook tijdens het uitvoeren van een Build, zeker in het geval je zogenaamde "Continious Integration" (CI) build hebt aangezet op een project. Daarbij is namelijk het doel zo snel mogelijk een build te maken die aantoont of een change mogelijk problemen veroorzaakt in de totale codebase. Door nu in een CI build alleen de "aanbevolen" testen uit te voeren kan een minimale set unit testen worden gebruikt voor het aantonen van regressie in de build. Dit stelt ons in staat om een CI build zeer efficiënt in te richten. Visual Studio Team Test Edition Een van de meest in het oog springende nieuwe features is, denk ik, de nieuwe test tools die beschikbaar komen. In de huidige (2008) Visual Studio Team Test editie is heel duidelijk de "technische" tester de doelgroep. In Rosario heeft men ook heel duidelijk de functioneel tester in het vizier. Dit maakt men mogelijk door de introductie van een nieuwe toolset, met als doelgroep functioneel tester, die buiten de Visual Studio IDE wordt gebruikt. Deze applicatie heeft de naam "Camano". "Camano" is een applicatie die ondersteunt in het opzetten, beheren, plannen en uitvoeren van testen. Hierbij introduceert Microsoft een aantal termen die overal in de tooling terug komen, te weten: Test Case, Test Suite, Test Configuration, Test Plan en Test Pass. De Functioneel Tester wordt niet langer vergeten Een test case bevat de stappen om een specifiek onderdeel van een applicatie te testen. Een test case is een workitem dat o.a. de stappen bevat die in een test moeten worden uitgevoerd. Meerdere testcases worden samengevoegd in een test suite, voordat er een test kan worden uitgevoerd. Een test suite kan je in "Camano" samenstellen uit een aantal te selecteren test cases of door middel van een query. Dit laatste noemt men dan een dynamic test suite. Test configurations zijn aanduidingen van omgevingen waar een test wordt uitgevoerd, b.v. Windows Vista SP1 met FireFox browser, of Windows XP SP3 met IE7. Test suites kun je samen met een test configuration gaan inplannen om uit te voeren door de groep testers. Hierbij kun je specifieke testen toekennen aan testers en kun je het test plan activeren zodat de benodigde workitems op de naam van een betreffende tester worden gezet. Het uitvoeren van een test wordt een test pass genoemd. Op een test pass wordt gerapporteerd wat de resultaten van de test waren. De resultaten worden door de tester tijdens het uitvoeren van de test aangegeven in de test tool "MSRun". Deze test tool dient ter ondersteuning tijdens de testuitvoer en biedt een aantal opties (zie figuur 2). Fig. 2: MSRun test applicatie 20 MAGAZINE

21 .NET De MSRun test tool kent een aantal opties, waaronder het op de achtergrond opnemen van de acties die zijn uitgevoerd, het vastleggen van de machine instellingen zoals CapsLock, Num- Lock, service packs, etc., het opnemen van een zogenaamde automation strip, etc. Al deze opties resulteren in losse logs die bij het vastleggen van een bug ook de omstandigheden duidelijk maken waaronder de fout is opgetreden. Er wordt zelfs een video-opname gemaakt van wat de tester allemaal heeft uitgevoerd. Bij het rapporteren van een bug worden al deze gegevens opgeslagen bij het workitem, zodat de ontwikkelaar direct kan zien waar het probleem zich heeft voorgedaan. In de toekomst komt daar ook de Protheus log bij, zodat de ontwikkelaar ook meteen door de historische data van de testrun kan stappen om zo het probleem zo snel mogelijk boven tafel te krijgen. Gedurende het uitvoeren van de testen is één van de opties het opnemen van een zogenaamde automation strip. Deze automation strip kan voor twee doeleinden worden ingezet: ten eerste voor de ondersteuning van een hertest en ten tweede voor het automatiseren van de test als een Coded UI Test. Voor de ondersteuning van de handmatige testen kan de automation strip worden ingezet voor het afspelen van stappen die in een voorgaande run ook al eens zijn uitgevoerd. Stel je bijvoorbeeld eens voor dat een testcase 25 stappen bevat en je alleen de laatste stap opnieuw moet controleren. Je kunt dan de test starten, de opgenomen test automation strip uit de vorige test pass gebruiken om automatisch de stappen tot en met stap 24 voor je te laten uitvoeren. Dit wordt gedaan door in het MSRun tool te kiezen voor de playback van de automation strip tot een aangegeven punt. Vervolgens hoeft de tester alleen de laatste stap nog zelf uit te voeren. Deze manier van werken zorgt er voor dat voor regressie testen de scenario's op exact dezelfde manier worden doorlopen en zorgt daarnaast voor een stukje werkgemak voor de tester. Fig.3: Camano test pass details zorgen dat de historie van een change set beter inzichtelijk te maken is. Momenteel komt het nogal vaak voor dat je niet duidelijk kunt zien of een bepaalde change nu wel of niet onderdeel is geworden van een specifieke branch. Daarom maakt men een optie voor het visualiseren van de history van een change set. Op die manier wordt dan inzichtelijk in welke branch de change set op een gegeven moment in de tijd aanwezig is. Dit is zeer nuttig om bijvoorbeeld inzichtelijk te maken in welke versie van een product een bugfix terug te vinden is. In figuur 4 is weergegeven hoe deze visualisatie er uit zal komen te zien: Zoals aangegeven, kun je de automation strip ook inzetten voor het verder automatiseren van test cases. Hiervoor kun je dan een test toevoegen aan een test project van het type "CodedUItest". Dit lijkt erg op een unit test, echter is dit specifiek bedoeld voor UI test automation. In deze test kun je de automation strip gebruiken om code te laten genereren die de stappen volledig automatisch voor je uitvoert. Je kunt dan tevens een zogenaamd verification point toevoegen, die bijvoorbeeld de waarden uit een control uitleest en kan vergelijken met verwachte waarden. Op deze manier kun je relatief eenvoudig de UI testing volledig automatiseren en de kosten van het regressietesten significant reduceren. De test tools zijn primair bedoeld voor de functioneel testers. Dit is ook direct herkenbaar doordat de tester niet Visual Studio hoeft te gebruiken voor het maken van de test cases, suites, configurations en plans. Ook de rapportage over een test pass wordt volledig in Camano gedaan en geeft een grafisch overzicht van de resultaten tot op dat moment voor een betreffend test plan. Camano zelf is volledig in WPF geschreven en geeft door middel van de Team Foundation Server de mogelijkheid op een juiste manier in het team samen te werken. De test cases, plans en results worden allemaal opgeslagen in Workitem Tracking en een aparte Team Test Database. Op die manier worden ook de resultaten t.a.v de historie keurig bijgehouden. In figuur 3 is een screenshot van Camano weergegeven, waarbij de resultaten van een test pass worden getoond. Team Foundation Server Als laatste wil ik nog heel kort kijken naar de Team Foundation Server. Ook aan die kant worden de nodige verbeteringen doorgevoerd. In bijvoorbeeld Version Control wordt er hard gewerkt om er voor te Fig. 4: Change set visualization Verder werkt men ook hard om zgn. conflict resolving beter voor ons op te lossen. Hierbij is het doel om zoveel mogelijk scenario's te automatiseren, zodat de tooling de juiste keuzes maakt en de ontwikkelaar dit alleen nog hoeft te controleren. Beter inzicht in change set history Als laatste wil ik ingaan op veranderingen die worden gemaakt t.a.v. de Team Build omgeving. In de 2008 versie is er al een aardige aanpassing geweest aan de architectuur van Team Build. Toch heeft men er voor gekozen om ook in "Rosario" een significante verandering in de aanpak te kiezen. Een van de veelgevraagde mogelijkheden is namelijk de optie om niet een eenmalig script te genereren voor de build, maar dat de build definitie continue aanpasbaar blijft vanuit de IDE. Daarbij wil men ook graag dat de build-stappen gevisualiseerd worden. Om die reden heeft het team ervoor gekozen de toekomstige team-build-omgeving wederom volledig opnieuw te bouwen. Deze keer doet men dit op basis van Windows Workflow Foundation (WF). magazine voor software development 21

22 .NET Door middel van WF kan men namelijk naast de eenvoudige buildvisualisatie (Workflow designers) ook een veel robuustere runtimebuild-omgeving realiseren. Deze heeft dan standaard ondersteuning voor zaken als parallel uitvoeren van stappen en het distribueren van stappen over machines heen. Last but not least, geeft dit ook de oplossing voor het aanpasbaar houden van de buildstappen door middel van een editor. In Team Build maakt men het mogelijk een build uit te laten voeren door een op dat moment vrije buildmachine uit een pool aan machines. Hierbij wordt gekeken of een buildserver uit de pool voldoet aan gestelde randvoorwaarden alvorens deze wordt gebruikt om de build te queuen. Het selecteren van een machine uit de pool wordt op basis van zogenaamde Tags gedaan. Door Tags aan een server te hangen en aan een build-definitie kan dan een beschikbare server gekozen worden uit de pool met machines. Conclusie Al met al kunnen we stellen dat Team System "Rosario" een significant aantal nieuwe features kent. De focus bij al deze features ligt in het feit dat ze ondersteuning moeten bieden aan het eerder genoemde thema. Door middel van het toevoegen van architectuurtools als UML en een architectuur explorer maakt men het mogelijk ook in een Microsoft omgeving eerst de concepten op te schrijven in diagrammen en vervolgens pas te starten met het implementeren. Met behulp van de nieuwe Team Developer features maakt men het mogelijk sneller fouten op te sporen, wat ons productiever maakt. Door middel van de nieuwe test tools kan beter worden gecontroleerd of alles wat we hebben gemaakt, in overeenstemming is met de afspraken die we hebben gemaakt. Door vervolgens al deze features naadloos op elkaar aan te sluiten maakt Microsoft het mogelijk applicaties steeds efficiënter te ontwikkelen en de kosten voor realisatie en onderhoud in de toekomst verder te reduceren. Voor mijzelf is "Rosario" zeker een versie waar ik nu al naar uitkijk. Zodra de officiële bèta's beschikbaar komen weten we beter waar het product daadwerkelijk naartoe gaat en of de features die we tot nu toe hebben gezien, ook daadwerkelijk in het eindproduct terug te vinden zullen zijn Marcel de Vries Marcel de Vries is IT-Architect bij Info Support voor de Business unit Finance en MVP. Marcel heeft na vele jaren ervaring opgedaan met het.net platform bij het bouwen van Enterprise administratieve applicaties voor grote bedrijven in Nederland. Naast het schrijven van artikelen voor SDN en Microsoft.NET magazine is hij een veel gevraagde spreker op seminars en conferenties waaronder Microfsoft Tech-Ed, Developer Days en SDC. Naast zijn werkzaamheden bij diverse klanten geeft Marcel trainingen bij Info Support in het.net curriculum. Verder is hij bij Info Support architect binnen het innovatie team van Professional Development Center waar de Info Support software ontwikkelstraat wordt gemaakt en beheerd. Vanuit deze rol is hij mede verantwoordelijk voor het opdoen van kennis over de nieuwste technologieën zodat deze kunnen worden toegepast in de ontwikkelstraat en verder kan worden uitgedragen binnen en buiten het bedrijf. Advertentie 4Dotnet b.v.

23 ARCHITECTURE Bert Loedeman en Steef-Jan Wiggers Zeven Windows Communication Foundation Best Practices Windows Communication Foundation (WCF) is een nieuwe technologie voor het bouwen van servicegeoriënteerde applicaties. Het kan onder andere gebruikt worden voor het opzetten van een Service Oriented Architecture (SOA), maar ook voor simpelere scenario s van communicatie tussen twee applicaties op dezelfde machine tot processen op verschillende machines. In dit artikel beschrijven de auteurs een aantal best practices en wat je moet overwegen wanneer je WCF gaat toepassen. WCF kan worden toegepast wanneer je twee verschillende stukken code met elkaar wilt laten communiceren. Daarbij kun je aan verschillende scenario s denken. Een mogelijk scenario is interoperabiliteit, waarbij een servicegeoriënteerde applicatie gemaakt kan worden die over het internet communiceert met andere services, geschreven in een totaal andere technologie zoals Java. Een ander mogelijk scenario op bescheidener schaal kan zijn wanneer je twee klasses in dezelfde assembly in hetzelfde proces met elkaar wilt laten praten. Samengevat kun je WCF gebruiken voor elk nieuw stuk code waar je over de grens van processen heen wilt of wanneer je binnen hetzelfde proces wilt verbinden met ontkoppelde objecten (zoals services). Je kunt hierbij het bestaan van.net remoting, ASP.NET Web Services en Enterprise Services vergeten en je richten op WCF voor alle mogelijke communicatiebehoeften. Voor het bouwen van WCF services is een aantal best practices van toepassing. Met toepassing van deze practices kan men effectieve, veilige en betrouwbare services bouwen. In dit artikel zullen zeven practices worden toegelicht: Gelaagdheid Instantiëring van aanroep Omgaan met fouten Kiezen van de juiste service host Verantwoorde manier van callbacks Gebruikmaken van onderhoudbare proxy code Beveiliging Voordat we ingaan op de best practices leggen we kort uit hoe communicatie binnen WCF werkt. Server Verbinden van twee stukken code met WCF vereist dat je eerst een aantal zaken regelt aan de server kant. Ten eerste heb je een servicecontract nodig. Deze definieert de operaties die je wilt aanbieden via de service, samen met de data die er doorheen loopt. Vervolgens zijn datacontracten nodig voor complexe types die door de service operaties heen gaan. Deze contracten bepalen de structuur van de verzonden data, zodat deze data kan worden geconsumeerd of geproduceerd door de client. Daaropvolgend heb je een service implementatie nodig. Hier bevindt zich functionele code, die de vraag beantwoordt van binnenkomende berichten en wat er mee moet gebeuren. Naast implementatie, service- en datacontract is een configuratie van de service vereist. Binnen de configuratie wordt gespecificeerd hoe de service wordt blootgesteld in termen als address, binding en contract (het abc). De binding bijvoorbeeld bevat informatie over: welk netwerkprotocol gebruikt gaat worden; codering van het bericht; eventuele beveiligingsmechanismes die worden toegepast en of gebruikt gemaakt wordt van betrouwbaarheid (reliability); transacties of andere mogelijkheden die WCF ondersteunt. Ten slotte is een host nodig om de service te kunnen laten werken. De zogenaamde service host kan een.net proces zijn dat zelf gehost wordt in bijvoorbeeld een Windows console applicatie, IIS, of Windows Activation Service (WAS) voor Vista en Windows Server Client Aan de kant van de client, de service-consument, zijn drie zaken nodig om met WCF een service aanroep te kunnen uitvoeren, waarbij het niet uitmaakt of de service met WCF of met een andere technologie is geïmplementeerd: 1. Een service-contract definitie die overeenkomt met wat aan de server kant gebruikt wordt; 2. Een data-contract definitie overeenkomstig met wat aan de server kant gebruikt wordt; 3. Een proxy om berichten te maken om naar de service te versturen en antwoordberichten te verwerken. Figuur 1 geeft de situatie weer tussen de service aan de server kant en de client als consument in twee scenario s zoals eerder als voorbeeld aangegeven. Fig. 1: Communicatie tussen service en client magazine voor software development 23

24 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES ARCHITECTURE Scheiding in lagen Gelaagdheid is een belangrijk principe binnen het ontwikkelen van software. Het scheiden van functionaliteit in presentatielaag, businesslaag, en data(toegangs)laag is al jaren bekend. Het opdelen in lagen levert scheiding op van verantwoordelijkheden, makkelijker te onderhouden code en betere schaalbaarheid bij fysieke scheiding van de lagen. In de datatoegangslaag, beter bekend als Data Access Layer (DAL), ligt de verantwoordelijkheid van de vertaling tussen de database en het applicatiedomein. Een dergelijke verantwoordelijkheid is ook nodig in een aparte service laag die zich richt op vertaling tussen applicatiedomein en de servicegeoriënteerde buitenwereld (zie figuur 2). Gelaagdheid is een belangrijk principe binnen het ontwikkelen van software Fig. 2: Scheiding in lagen Een servicelaag impliceert dat de servicedefinitie zich in een aparte klassenbibliotheek bevindt, welke wordt gehost in een host omgeving zoals IIS. Binnen de servicelaag worden aanroepen doorgezet naar de businesslaag om de gevraagde operatie te verwerken. WCF maakt het mogelijk je servicecontract en operatiecontract attributen direct in je serviceklasse te stoppen. Dit is echter iets wat je moet zien te voorkomen. Het is beter de interfacedefinitie, die duidelijk definieert wat je service boundary is, af te zonderen van de implementatie van de service. Voor een simpele service ziet je contractdefinitie eruit zoals in listing 1. [ServiceContract()] public interface IProductService [OperationContract()] List<ConsumerProduct> GetProducts(); Listing 1 Een belangrijk aspect van serviceoriëntatie is het verbergen van alle details van je implementatie achter een service boundary. Dit geldt eveneens voor de technologie die je gebruikt voor de implementatie. Daarbij is het niet wenselijk dat je aanneemt dat de applicatie een complex datamodel ondersteunt. Een deel van je service boundary definitie is de datacontractdefinitie voor (complexe) types, welke als operatieparameters kunnen worden doorgegeven aan of kunnen worden teruggestuurd door de service Voor maximale interoperabiliteit en aansluiting met principes van serviceoriëntatie zul je niet specifiek.net types, zoals DataSets of excepties, moeten versturen buiten je service boundary. Je moet je houden aan simpele datastructuurobjecten zoals klassen met properties en achterliggende member-velden. Je kunt objecten laten passeren die geneste complexe types bevatten, zoals klant en order collecties. Je mag daarbij echter niet aannemen dat een klant objectgeoriënteerde constructies als interfaces of basisklassen ondersteunt ten behoeve van interoperabiliteit met webservices, al dan niet geïmplementeerd met een andere technologie. Als je WCF alleen als nieuwe manier van remoting technologie wilt toepassen om twee stukken.net code met elkaar te laten communiceren over een proces met de verwachting of eis voor andere om consumerende applicaties te schrijven, dan kun je alle.net types gebruiken in je datacontract. Je zult er voor moeten zorgen dat dergelijke types wel gemarkeerd zijn als datacontracten of te serialiseren zijn. Eigenlijk sta je voor diverse uitdagingen wanneer je datasets door WCF laat passeren, wat je het beste kunt vermijden (behalve in zeer eenvoudige scenario s). Als je datasets wilt toepassen in WCF, werk dan met getypeerde datasets waarbij je moet trachten zelf getypeerde datasets als parameters of terugkerende waarde te gebruiken. Zoals eerder beschreven bevat een simpel contract een type als: List<CustomerProduct>. WCF is ontworpen om enumerable collecties 'plat te slaan' in arrays op de service boundary. In plaats van het verminderen van interoperabiliteit maakt dit je leven eenvoudiger wanneer je collecties vult en gebruikt in de service zelf en in de businesslagen. Kijk eens naar de datacontract definitie voor het CustomerProduct- Type in listing 2. [DataContract()] public class ConsumerProduct private int productid; private string productname; private double unitprice; [DataMember()] public int ProductID get return productid; set productid = value; [DataMember()] public string ProductName get return productname; set productname = value; [DataMember()] public double UnitPrice get return unitprice; set unitprice = value; Listing 2 Enkele instantie per aanroep Een best practice is een instantie per aanroep in te stellen als default. WCF ondersteunt drie instantiemogelijkheden voor services, namelijk: 1. Per-Call; 2. Per-Session; 3. Single. Per-Call houdt in dat een nieuwe instantie van een serviceklasse wordt gecreëerd voor elke aanroep die de client doet van een operatie. Deze instantie wordt opgeruimd wanneer deze afgerond is. Dit is de meest schaalbare en robuuste optie. Per-session betekent dat client de service-instantie gaande houdt op de server zolang de client aanroepen blijft doen aan de service. Hiermee kun je de state van een service vasthouden in membervariabelen van de service-instantie. Je bent daardoor in staat stateful conversatie tussen client-applicatie en server-object te doen. Dit heeft echter wel een aantal neveneffecten, waaronder geheugengebruik op de server, waarmee de schaalbaarheid wordt beïnvloed. Ten slotte stelt de Single(ton) mogelijkheid je in staat aanroepen aan de service van alle clients te routeren naar een enkele instantie van de service op de server. Hierdoor verkrijg je één enkel punt als zogenaamde 'poortwachter'. Deze mogelijkheid is slecht voor de 24 CONFERENCE MAGAZINE EVENTS

25 ARCHITECTURE schaalbaarheid, omdat alle aanroepen in de singleton instantie geserialiseerd worden (één voor één). Singleton routeert alle calls naar een enkele instantie van de service Single en Per-Session delen vergelijkbare neveneffecten, zoals schaalbaarheid. De beste optie voor ontwerp van services is Per-Call, omdat het de schoonste, veiligste en meest schaalbare optie is. De andere opties hebben alleen nut wanneer je extra mogelijkheden nodig hebt van deze specifieke opties, zoals een stateful mechanisme. De code in listing 3 declareert een Per-Call service. [ServiceBehavior( InstanceContextMode=InstanceContextMode.PerCall)] public class ProductService : IProductService // Code Listing 3 Omgaan met fouten Afhandeling van fouten is altijd al een belangrijk onderwerp geweest in het maken van applicaties. Dit geldt ook voor het maken van services met behulp van WCF. Wanneer een niet afgehandelde fout een service boundary bereikt, zal WCF deze opvangen en een SOAP fout retourneren naar de aanroepende partij. Standaard is deze fout nietszeggend en geeft deze geen details over het precieze probleem en de oorzaak ervan. Op zich is hier niets mis mee: het komt overeen met de ontwerpprincipes van serviceoriëntatie. Standaard toon je alleen die informatie aan de consument van een service die je wilt tonen. Je wilt voorkomen dat details als stack-traces, die normaliter bij foutafhandeling voorkomen, worden getoond. De architectuur van WCF is dusdanig dat je deze in twee lagen kunt opsplitsen, namelijk de channellaag en servicemodellaag. De channellaag is verantwoordelijk voor de communicatie-infrastructuur en de servicemodellaag voor een objectgeoriënteerd en declaratief programmeermodel. Figuur 3 geeft beide WCF architectuurlagen weer. handelt alle fouten af en gooit een FaultException<T> exceptie. Dit geldt in alle gevallen wanneer een service van een exceptie kan herstellen en volgende aanroepen zonder problemen kan afhandelen. De FaultException<T> is een speciaal.net type dat zich binnen de.net call stack gedraagt als een normale exceptie. Deze wordt echter anders geïnterpreteerd door de WCF laag die de messaging verzorgt. Je kunt het beschouwen als een afgehandelde exceptie, die naar WCF wordt aangeleverd in plaats van een normale exceptie. Deze exception propageert zichzelf in WCF zonder interventie van de service. Je kunt gedetailleerde gegevens achterwege laten richting de client over de reden van het probleem door gebruik te maken van de Reason property van het FaultException<T> type. Als een FaultException<T> wordt opgevangen door WCF, zal deze worden verpakt als een SOAP fault message, maar binnen het channel zal er geen fout plaatsvinden. Dit betekent dat je de client kunt instrueren de opgetreden fout af te handelen en de service kunt blijven benaderen zonder een nieuwe connectie op te zetten. Er zijn veel verschillende scenario s denkbaar voor het afhandelen van excepties in WCF alsmede voor de manier waarop je de foutafhandeling in de servicelaag wilt regelen. De volgende basisregels moet je altijd hanteren: Vang alle niet afgehandelde excepties in WCF af en werp een Fault- Exception<T> op als je service in staat is te herstellen en volgende calls zonder problemen kan afhandelen (dit moet mogelijk zijn als je instantie per aanroep in stelt (Per-Call)); Stuur geen exceptiedetails naar de client behalve voor debugging doeleinden tijdens ontwikkeling; Stuur geen gedetailleerde informatie naar client gebruikmakend van de reason Property van de FaultException <T>. Listing 4 laat zien hoe een servicemethode een exceptie afvangt. Daarbij wordt geen gedetailleerde informatie van het opgetreden probleem naar de client gestuurd middels de FaultException <T>: List<ConsumerProduct> IProductService.GetProducts() try ProductManager prodmgr = new ProductManager(); return ConsumerProductEntityTransformer. GetConsumerProducts( prodmgr.getproductdetaillist()); catch (SqlException ex) throw new FaultException<string>( The service could not connect to the data store, // detailinformatie, Unknown error // reden); Listing 4 Fig. 3: Architectuur van WCF De channellaag is volledig te configureren en uit te breiden. De binding is hierop van toepassing. Een niet afgehandelde exceptie zal in de channellaag resulteren in een fout, nog voordat de service zelf bereikt wordt. Dit houdt in dat je een volgende aanroep van de client niet door dezelfde proxy kunt laten verlopen. Er zal een nieuwe connectie naar de server moeten worden gemaakt. Dit heeft tot gevolg dat je een goede servicelaag zult moeten ontwerpen. Deze servicelaag De generic type parameter T kan elk gewenste type zijn, maar in het kader van interoperabiliteit wil je het liefst een simpele datastructuur gemarkeerd als datacontract of als types die te serializeren zijn. Kies de juiste service host WCF kent drie opties voor hosten van een service. Men kan zelf de service hosten (draaien van je services in elke gewenste.net applicatie), hosten binnen IIS of gebruik maken van Windows Activiation Services (WAS). magazine voor software development 25

26 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES ARCHITECTURE Zelf hosten binnen een applicatie (self-hosting) geeft je de meeste flexibiliteit, omdat je zelf de hosting-omgeving opzet. Dit betekent dat je volledig toegang hebt tot je hosting-omgeving en dat je de configuratie zelf kunt coderen. Ook kun je zaken zoals operatiemonitoring en andere controles aanhaken aan de service door middel van events. Het zelf hosten van de service legt echter ook de verantwoordelijkheid van procesmanagement en andere configuratiemogelijkheden bij de bouwer. Listing 5 beschrijft een dergelijk scenario van self-hosting: public partial class MyServiceHost : Servicebase ServiceHost productservicehost = null; public MyServiceHost() InitializeComponent(); protected override void OnStrart(string args[]) productservicehost = new ServiceHost(typeof(ProductService)); productservicehost.open(); protected override void OnStop() if(productservicehost!= null) productservicehost.close(); Listing 5 Hosten in IIS stelt je in staat je services uit te rollen in IIS door simpel weg de DLL te plaatsen in de \BIN folder en de.svc files als service adresseerbare endpoints te definiëren. Hiermee verkrijg je op kernelniveau request routering van IIS, de IIS management consoles voor configureren van de hosting-omgeving, de IIS mogelijkheid van starten en hergebruiken van werkprocessen, en nog veel meer. Het grote nadeel van hosten in IIS (tot en met IIS 6) is dat je beperkt bent tot HTTP gebaseerde bindings. In de toekomst vervalt deze beperking met IIS 7.0 en WAS. WAS is de beste keuze om nieuwere platformen te ondersteunen WAS is een onderdeel van IIS 7 (Windows Vista en Windows Server 2008) en het geeft je een hosting-model waarbij je jouw services kunt blootstellen (aanbieden) met andere protocollen dan HTTP, zoals TCP, Named Pipes en MSMQ. WAS is altijd de beste keuze als je nieuwere platformen gaat ondersteunen. Als je services buiten je intranet gaat aanbieden, zul je over het algemeen HTTP gaan gebruiken, dus IIS is het beste voor hosten van externe services. Als WAS hosting geen optie is voor draaien van services intern op het intranet, dan kun je overwegen zelf de services te hosten. Dit heeft als voordeel dat andere (snellere en meer capabele) protocollen binnen je firewall kunt gebruiken zodat het je meer flexibiliteit geeft ten aanzien van configuratie van je eigen omgeving. Verantwoord gebruik van callbacks WCF bevat een eigenschap om een callback te doen naar een client om asynchroon data te routeren of als een vorm van eventsignalering. Dit is handig wanneer je aan de client kenbaar wilt maken dat een lang lopende operatie klaar is in bijvoorbeeld je backend, of dat je een client op de hoogte wilt brengen van verandering in data die gevolgen kan hebben voor de client. Om dit voor elkaar te krijgen moet je een callback-contract definiëren dat is gekoppeld (overeenkomt met) aan je servicecontract. De client hoeft het callback-contract niet publiek te maken aan de buitenwereld als een service, maar de service kan het gebruiken om een callback te doen naar de client nadat de initiële aanroep reeds is gedaan vanuit de client naar service. Listing 6 creëert een servicecontract-definitie met daaraan gekoppeld een callbackcontract. [ServiceBehavior( InstanceContextMode=InstanceContextMode.PerCall)] public class ProductService : IProductService #region IProductService Members List<ConsumerProduct> IProductService.GetProducts() try ProductManager prodmgr = new ProductManager(); return ConsumerProductEntityTransformer. GetConsumerProducts( prodmgr.getproductdetaillist()); catch (SqlException ex) throw new FaultException<string>( Service could not connect to the data store, Unknown error ); #endregion Listing 6 Als je van plan bent callbacks te gebruiken, is het een goede over weging het aan te bieden via een publish/subscribe API als onderdeel van het servicecontract. Voor uitvoeren van een callback zal de service de context van een binnenkomende aanroep moeten opvangen en vasthouden in het geheugen tot het moment dat een aanroep terug heeft plaatsgevonden naar de client. De client zal een object moeten aanmaken, dat een callback-interface implementeert om binnenkomende aanroepen van de service te kunnen ontvangen. De client zal vervolgens dit object en haar proxy levend moeten houden zolang callbacks verwacht kunnen worden. Dit is een sterk gekoppeld communicatiemechanisme tussen de client en de service (inclusief objectafhankelijkheden), dus is het een goed idee om de client de controle te verschaffen over wanneer de koppeling start en eindigt door middel van expliciete serviceaanroepen die aanvangen en eindigen bij een conversatie. Grootste limiterende factor van callbacks is het niet kunnen schalen en mogelijk niet werken in interoperabiliteit-scenario s. Het probleem met schalen is gerelateerd aan het feit dat de service een referentie moet hebben naar de client die in het geheugen wordt vastgehouden om een callback te kunnen uitvoeren op de referentie. Listing 7 illustreert hoe je client-notificatie kunt opvangen, opslaan en veranderen door middel van een callback-referentie voor een service. class ProductServiceWithCallbacks : IProductService2 static List<IProductServiceCallback> subscribers = new List<IProductServiceCallback>(); 26 CONFERENCE MAGAZINE EVENTS

27 ARCHITECTURE #region IProductService2 Members List<Product.Entities.ConsumerProduct> IProductService2.GetProducts() throw new NotImplementedException(); void IProductService2.SubscribeProductChanges() IProductServiceCallback client = OperationContext.Current. GetCallbackChannel<IProductServiceCallback>(); lock (subscribers) subscribers.add(client); void IProductService2.UnsubscribeProductChanges() IProductServiceCallback client = OperationContext.Current. GetCallbackChannel<IProductServiceCallback>(); lock (subscribers) subscribers.remove(client); #endregion static void NotifyClientsProductChanged (ConsumerProduct product) lock (subscribers) foreach (IProductServiceCallback client in subscribers) client.productchanged(product); Listing 7 Je zult interoperabiliteitproblemen tegenkomen omdat je callbackmechanisme is gebaseerd op Microsoft WCF technologie, en die is niet standaard. Het is een vendor-specifieke standaard, maar bepaalde zaken zijn uitgedrukt binnen een SOAP-bericht dat geconsumeerd of gebruikt kan worden door andere technologieën. Dus als interoperabiliteit een deel van de wensen en eisen van de klant zijn, kun je callback beter vermijden. Een alternatief kan zijn om een polling-api op te zetten waar een client komt vragen om veranderingen op bepaalde tijdstippen, of je kunt een publish/subscribe middleman service opzetten als broker subscriptions en publicaties om koppeling tussen client en service te voorkomen zodat deze wel te schalen zijn. Een van de principes van serviceoriëntatie is dat je schema s en contracten deelt, geen types je niet twee typedefinities voor hetzelfde maken. Het kan geen kwaad aan de clientzijde een referentie aan te leggen met een assembly die ook door de service wordt gebruikt om toegang te krijgen tot.net typedefinities van service- en data-contracten. Je moet ervoor zorgen dat de service bruikbaar is voor de client als ze niet beide toegang hebben tot een gedeelde assembly; dat is beter dan het hergenereren van dergelijke types aan de clientzijde op basis van metadata. Om dit te doen definieer je de types in een aparte assembly van de service-implementatie, zodat zowel de client als service een referentie kunnen leggen zonder additionele koppeling. Als je dit doet, introduceer je meer afhankelijkheid tussen client en service, maar dan vanwege productiviteit en snelheid op gebied van ontwikkeling en onderhoud. Zoals eerder bij het stukje over het omgaan met fouten is beschreven, kan een niet afgehandelde fout in een channel ook een fout opleveren. Dit geldt voor de meeste bindings. Wanneer een fout wordt geleverd aan een WCF client, wordt er een FaultException gegenereerd als het niet als FaultException<T> op de service zijde is geïntroduceerd. Dit is echter niet voor elke binding consequent zo geregeld. Je wilt dat je service- en client-code zijn ontkoppeld, onafhankelijk van je binding. Het enige veilige dat je aan de client kant kunt doen is uitgaan van het een worst-case scenario als de service een exceptie gooit. In deze situatie vermijd je dat de proxy opnieuw wordt gebruikt. Het weggooien of sluiten van de proxy kan resulteren in een volgende exceptie. Dit betekent dat je de calls naar een service in een try/catch blok moet zetten en de proxy-instantie moet vervangen met een nieuwe in het catch blok, zoals te zien is in listing 8. public class MyClient ProductServiceClient proxy = new ProductServiceClient(); public Window1() InitializeComponent(); private void OnGetProducts( object sender, RoutedEventArgs e) try DataContext = proxy.getproducts(); catch (Exception ex) proxy = new ProductServiceClient(); Listing 8 Gebruik maken van onderhoudbare proxy code Een van de principes van serviceoriëntatie is dat je schema s en contracten deelt, geen types. Het je zo min mogelijk afhankelijk maken van de client van de servicedefinitie is daarbij noodzakelijk. Geen types buiten de service en de client die geen deel uitmaken van de service boundary. Echter, wanneer je zowel de service als de client schrijft, wil Beveiliging Tot slot moet beveiliging rond WCF niet vergeten worden. Beveiliging van je applicatie en dus ook van een service is belangrijk om oneigenlijk gebruik of het vrijkomen van gevoelige data te voorkomen. In dit artikel gaan we alleen in op de beveiliging van TCP en SOAP verkeer. Hierbij speelt een aantal WCF-bindings een rol, waarvan de magazine voor software development 27

28 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES ARCHITECTURE meest gebruikte zijn: NetTcpBinding (intranet applicaties), BasicHTTPBinding en WSHTTPBinding (internet applicaties). De beveiliging van een WCF service kan worden onderverdeeld in twee soorten beveiliging, namelijk het beschermen van berichten tegen ongewenste meelezers (transportbeveiliging en berichtbeveiliging) en het voorkomen dat berichten meer dan eens worden aangeboden (beveiliging tegen replay-attacks). De beveiliging van een WCF applicatie gebeurt altijd per binding. Beschikbare beveiligingsopties verschillen per binding; een overzicht hiervan is te vinden op de volgende locatie: Transportbeveiliging en berichtbeveiliging Beveiliging van dataverkeer is mogelijk door te kiezen voor transportbeveiliging, berichtbeveiliging of een combinatie van beide. Transportbeveiliging is in de meeste gevallen efficiënter dan berichtbeveiliging. Transportbeveiliging heeft te maken met het risico dat tussenstations voor het verkeer deze beveiliging kunnen verbreken. Berichtbeveiliging is minder efficiënt, maar biedt wel end-to-end beveiliging. De keuze voor één van beide technieken is sterk verbonden met de omgeving waarin het dataverkeer plaats vindt. Intranet applicaties Bij applicaties die op een intranet worden geïnstalleerd, is het verstandig om te kiezen voor een TCP binding met transport - beveiliging. Ten eerste is binair transport over TCP significant sneller dan op SOAP gebaseerd transport over HTTP. Dit is onder andere te danken aan de mogelijkheid om zonder IIS een verbinding te kunnen opzetten. Bij een intranet applicatie is er veel controle over eventuele tussenstations in de verbinding, waardoor het risico op verbreking van de beveiliging zeer klein is. De NetTcpBinding voldoet standaard aan deze manier van datatransport. In listing 9 wordt een door middel van transportbeveiliging beveiligde NetTcpBinding uitgewerkt. Om een beveiliging uit te werken dient altijd een security-sectie onder de binding-sectie te worden aan gemaakt, waarin de beveiligingsmodus wordt weergegeven. In deze sectie moet de gekozen beveiliging door een subsectie worden uitgewerkt. <nettcpbinding> <binding name= nettcp > <security mode= Transport < <transport clientcredentialtype= Windows /< </security< </binding< </nettcpbinding Listing 9 Internet applicaties Applicaties die dataverkeer hebben dat over het internet wordt geleid, hebben weinig baat bij transportbeveiliging. Als een tussenstation de beveiliging wil verbreken, dan is daarop geen enkele controle. Het ligt daarom voor de hand om te kiezen voor berichtbeveiliging. Omdat het versturen van binaire data over het internet niet mogelijk is, ligt het voor de hand om te werken met SOAP over HTTP. Het hosten van een internet applicatie in IIS heeft als extra voordeel dat het beveiligingsmechanisme van IIS kan worden gebruikt. Ook is IIS sterk in het beheren van services en in het bieden van schaalbaarheid. Als data erg gevoelig is, is het raadzaam om naast berichtbeveiliging ook te kiezen voor transportbeveiliging. Hoewel transportbeveiliging over het internet geen sluitende beveiliging biedt, maakt het de job van een hacker een stuk minder aantrekkelijk. Houdt goed in de gaten dat transportbeveiliging voor dataverkeer over het internet nooit berichtbeveiliging zal kunnen vervangen. Als er een connectie moet worden gelegd met verouderde (legacy) clients, zoals clients die ASMX web services verwachten, dan is er geen andere optie dan te kiezen voor BasicHTTPBinding. Omdat de beveiligingsopties van de WS* serie (bekend van WSE 3.0) ontbreken, is dit de minst te beveiligen binding. Het is daarom nodig dat er goed wordt nagegaan of clients de WS* beveiligingsopties ondersteunen, zodat kan worden gekozen voor de WSHTTPBinding. Let er wel op dat de WSHTTPBinding standaard is geconfigureerd op berichtbeveiliging, terwijl verouderde clients met WS* ondersteuning in een aantal gevallen (veel niet-microsoft clients) enkel transportbeveiliging ondersteunen. Om een binding aan te maken met berichtbeveiliging, moet de sectie security worden aangemaakt met als modus Message, waarin een message-sectie moet worden aangemaakt. In listing10 is hiervan een voorbeeld uitgewerkt, waarbij ook het clientcredentialtype, de manier waarop de client zich verifieert bij de service, is gewijzigd van de standaard modus Windows naar verificatie met een certificaat. <wshttpbinding> <binding name= wshttp > <security mode= Message > <message clientcredentialtype= Certificate /> </security> </binding> </wshttpbinding Listing 10 Beveiliging tegen replay-attacks Als een WCF-service op de juiste manier is beveiligd met transportbeveiliging of berichtbeveiliging, wordt veelal vergeten dat er een risico is dat een onderschept bericht meermalen wordt aangeboden aan de service. In veel gevallen zal dit niet direct heftige problemen opleveren, maar in de volgende situaties is het van groot belang om te zorgen voor een beveiliging tegen deze zogenaamde replay-attacks: Een opnieuw aangeboden bericht veroorzaakt inconsistentie van data. Denk hierbij bijvoorbeeld aan het verwerken van betalings - gegevens; Een bericht wordt gerouteerd via onbetrouwbare tussenstations. Dit is eigenlijk altijd het geval bij berichtenverkeer via het internet; Verwerking van berichten is relatief gezien het meest intensieve proces van de applicatie. In deze situatie kan een hacker zeer eenvoudig zorgen voor een bottleneck binnen de service en zo een denial of service realiseren. Uit bovenstaande situaties blijkt dat het niet nodig is interne applicaties, bijvoorbeeld op een intranet, te voorzien van beveiliging tegen replay-attacks. In vrijwel alle gevallen waarbij het internet in het spel verschijnt, is het raadzaam om je tegen replay-attacks te Het is raadzaam om je tegen replayattacks te beveiligen beveiligen. Het principe achter de beveiliging tegen replay-attacks is vrij eenvoudig en verloopt zoals te zien is in figuur 4. Fig. 4: Beveiliging tegen replay-attacks 28 CONFERENCE MAGAZINE EVENTS

29 ARCHITECTURE De client-applicatie ondertekent het bericht met een unieke code. De service controleert het verzonden bericht aan de hand van deze unieke code. Dit vindt plaats door middel van een replay-cache waarin gedurende een bepaalde termijn binnengekomen codes worden opgeslagen. Wordt de code gevonden in de cache, dan wordt het bericht afgewezen, anders worden code en timestamp in de cache opgeslagen. Om in WCF gebruik te maken van beveiliging tegen replay-attacks, moet een custom binding worden aangemaakt. Hierin maak je een security-element aan. Vervolgens kan met een aantal properties de beveiliging worden geregeld. Beschikbare properties zijn: detectreplays: deze boolean property moet aan worden gezet om de beveiliging te laten functioneren. Dit is de standaardwaarde; maxclockskew: geeft het tijdsverschil aan dat wordt getolereerd tussen client en server. Standaard staat deze property ingesteld op vijf minuten, maar de beveiliging wordt strenger als deze tijd wordt ingekort; replaywindow: hiermee wordt ingesteld hoe lang een bericht wordt beschouwd als gevaarlijk met betrekking tot replaying. Berichten die binnen deze tijdspan vallen worden gecontroleerd; replaycachesize: het aantal unieke codes dat door de applicatie tegelijkertijd kan worden bijgehouden. Als het aantal berichten verzonden binnen de replaywindow groter is dan de cachegrootte, worden berichten geweigerd tot de cache weer ruimte heeft voor nieuwe unieke codes. Standaard kan de cache berichten herbergen. Een listing van het instellen van beveiliging tegen replay-attacks vanuit de configuratie is afgedrukt in listing 11. <custombinding< <binding name= NewBinding > <textmessageencoding /> <security> <localclientsettings replaycachesize= maxclockskew= 00:03:00 replaywindow= 00:03:00 /> <localservicesettings replaycachesize= maxclockskew= 00:03:00 replaywindow= 00:03:00 /> <secureconversationbootstrap /> </security> <HttpTransport /> </binding> </custombinding< Listing 11 Tot slot Met deze best practices tonen we aan dat het niet extreem ingewikkeld is om WCF in een applicatie te integreren. Hopelijk is aan de hand van dit artikel duidelijk geworden welke voordelen WCF biedt ten opzichte van het huidige.net 2.0 framework en hoe je deze technologie het best kunt toepassen. De beste manier om de inhoud van dit artikel te gebruiken is om de beschreven best practices te doorgronden en ze toe te passen in je persoonlijke 'speeltuin'. Bert Loedeman en Steef-Jan Wiggers Bert Loedeman is een ervaren, MCPD gecertificeerde,.net ontwikkelaar. Naast alles dat met.net te maken heeft, heeft ook SQL Server zijn interesse. Op beide gebieden heeft hij gedurende de afgelopen 4 jaren veel kennis opgedaan door het meedenken en meebouwen aan grote projecten in o.a. retailautomatisering en de verzekeringswereld. De laatste tijd concentreert hij zich steeds meer op het gebied van architectuur en inpasbaarheid van nieuwe technologieën binnen het Microsoft platform. Complexiteit is hierbij niet zozeer een beperking als wel een uitdaging. Naast het genieten van zijn werk als techneut, probeert hij zich sterk in te zetten voor het delen van kennis, bijvoorbeeld door het geven van cursussen. Steef-Jan Wiggers is een ervaren MCSD.NET gecertificeerde software ontwikkelaar en gecertificeerde MCDBA (SQL Server 2000) database ontwikkelaar, die in de loop der jaren een brede technische kennis heeft opgebouwd op het gebied van architectuur, systeem- en database ontwikkeling op het Microsoft platform. De afgelopen jaren heeft hij zich voornamelijk gericht op architectuur, integratievraagstukken, analyse en ontwerp en heeft zich het ontwikkelen met o.a. BizTalk, MSMQ en IBM MQ eigen gemaakt. Momenteel vindt hij dan ook zijn uitdaging in het ontsluiten van back-end systemen met behulp van de bovengenoemde technologieën, het ontwerpen van software architecturen en analyse van complexe business vraagstukken. Delphi TIP: Delphi 2009 $STRINGCHECKS OFF In Delphi 2009 is een speciale compiler setting te vinden genaamd STRINGCHECKS, die default "aan" staat. Deze compiler setting zorgt ervoor dat er voor Unicode Strings code gegenereerd wordt die compatible is met C++(Builder), wat belangrijk is als je bijvoorbeeld componenten of packages maakt in Delphi die door C++Builder gebruikt moeten worden. Echter, in de meeste gevallen worden er "gewoon" toepassingen gemaakt in Delphi 2009, zonder dat die door C++Builder gebruikt zullen worden. En omdat de STRINGCHECKS compiler setting wat inefficientere code genereert, is het aan te raden om deze compiler setting op OFF te zetten. Hoe doen we dat? Goeie vraag. De optie is zelf niet te vinden in de "Compiling" pagina van de Project Options dialoog, maar we kunnen hem wel opgeven in de "Additional switches to pass to the compiler" in the Other Options sectie van de Compiling pagina. Hier kunnen we "--string-checks:off" neerzetten (zonder de quotes), maar je kan dit ook in je source code opnemen: $STRINGCHECKS OFF De code die je hiermee krijgt is iets efficienter dan met string checks. magazine voor software development 29

30 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES.NET C# Dennis Vroegop C# Deep Dive Op de middelbare school zei mijn leraar Engels eens tegen mij: Engels is een taal die ontzettend makkelijk te leren is als je het een beetje wilt spreken, maar verd*%# lastig om goed te leren spreken. Het was een katholieke school, dus die *%# zei hij ook letterlijk zo. Deze uitspraak is me altijd bijgebleven. Ik denk dat dit een van de meest waardevolle lessen van de gehele middelbare school tijd is geweest. Deze uitspraak geldt namelijk niet alleen voor de taal Engels, maar voor heel veel dingen die we tegenkomen. Zeker in ons vakgebied zien we dit. Grote hoeveelheden ontwikkelaars kunnen zich een techniek heel snel eigen maken, maar als je doorvraagt of hun code goed bekijkt, merk je dat ze eigenlijk maar een klein stukje van die technieken beheersen. Om een techniek goed te beheersen moeten ze enorm veel tijd investeren; tijd die ze meestal domweg niet hebben. In de meeste gevallen is goed genoeg ook echt goed genoeg. In de meeste gevallen is goed genoeg ook echt goed genoeg Toch loont het de moeite om eens wat meer de diepte in te gaan om echt te weten wat er allemaal mogelijk is en hoe het allemaal precies werkt. Voor de taal C# geldt dit ook. De taal op zich is niet zo moeilijk. De constructies liggen voor de hand, er zijn niet al te veel keywords en je bent vrij snel in staat om aardige applicaties te schrijven. Maar om C# echt goed te beheersen zul je toch een langere tijd bezig moeten zijn met het uitzoeken van allerlei zaken. Laten we eens beginnen met de meest simpele kant van C#: de value en reference types. Value types en reference types Laten we eens bij het begin beginnen. De basis van een object georiënteerde taal zoals C# is dat je de meeste tijd bezig bent met het definiëren van types. Je maakt een hele verzameling classes die je al dan niet instantieert. Deze classes bevatten alle logica voor je applicatie. Maar classes zijn niet de enige types die je kunt maken: we hebben ook nog de structs. Voor veel ontwikkelaars is het verschil tussen beiden niet helemaal duidelijk. Wanneer gebruik je nu wat? Om het verschil duidelijk te maken moeten we eerst kijken naar de manier waarop de runtime met deze twee types omgaat. Classes zijn reference types. Dit houdt in dat de variabele die je declareert een pointer bevat, of liever: een referentie, naar het stuk geheugen dat je object in beslag neemt. Een struct echter is een value type: de variabele bevat in dit geval geen pointer maar daadwerkelijk de waardes die je wilt opslaan. Dit heeft een aantal consequenties; zo staan reference types op een andere plek in het geheugen dan de value types. Er zijn in je applicatie twee soorten geheugen beschikbaar: de heap en de stack. Nu wil ik hier niet ingaan op het geheugenbeheer van de CLR (we hebben immers een managed omgeving die het geheugen voor ons beheert) maar toch is enig begrip van deze twee noodzakelijk wil je het verschil tussen de reference type en de value type kunnen bevatten. De stack wordt gebruikt voor de code die op **dit** moment uitgevoerd wordt De stack en de heap Het komt er op neer dat de stack gebruikt wordt voor de code die op **dit** moment uitgevoerd wordt, terwijl de heap meer bedoeld is voor opslag van data voor de wat langere termijn. De stack is een kleiner stuk geheugen. Meestal staat de stack in de L2 cache van de processor; dit is een stuk geheugen dat enorm snel te benaderen is door de processor maar dat niet al te groot is. De heap echter is te vinden in het normale geheugen (optimalisatie door de compiler en runtime kunnen dit beïnvloeden, maar ga er maar even vanuit dat dit altijd zo is). Zoals je je kunt voorstellen is dit soort geheugen vele malen groter maar helaas ook vele malen trager te benaderen dan de stack. Neem als voorbeeld even de code in listing 1. public class MijnClass public int Getal; // en ergens anders public void MijnMethod() int x = 42; MijnClass c = new MijnClass(); c.getal = x; Listing 1 30 CONFERENCE MAGAZINE EVENTS

31 .NET C# We hebben hier een class, genaamd MijnClass en een method in een andere class met de naam MijnMethod. In MijnMethod declareren we een int x en zetten de waarde op 42. Deze x komt op de stack te staan. Immers, een variabele die we declareren en gebruiken in een method zal waarschijnlijk vaker gebruikt worden en moet snel te benaderen zijn. Daarnaast maken we een instance c van de class MijnClass. Nu gebeurt er iets bijzonders: op de stack komt een pointer te staan met daarin een verwijzing naar een stuk geheugen op de heap. In de heap komt de inhoud van het object te staan, in ons geval de variabele Getal (met als type int). Waarom gebeurt dit? Een class is een reference type Een class is een reference type. Dus we moeten de referentie naar de waardes (de int Getal) opslaan. Aangezien we dit object niet voor niets aanmaken, wordt de pointer naar de data in de stack opgeslagen. Op die manier kunnen we hem snel benaderen. De data zelf staat echter op de heap: classes kunnen groot worden en zoveel ruimte hebben we niet op de stack. Het geheugen ziet er nu dus als volgt uit: Fig. 2 en kan dus weg. Een ander voorbeeld: for(int i=0;i<42;i++)... De i hier is alleen maar van belang binnen de for-lus. Daarna kan hij weg. Alles wat op de heap staat is bedoeld voor de garbage collector. Deze objecten worden alleen opgeruimd als de runtime dat nodig vindt. Meestal is dat als de beschikbare hoeveelheid geheugen te klein wordt. Dit is niet veel anders dan hoe de meeste mensen werken: we ruimen kasten pas uit als we geen ruimte meer hebben voor de nieuwe spullen die we hebben gekocht. Toch? Een struct is een value type Een struct is een value type. Deze bevindt zich dus op de stack. Neem de volgende struct (listing 3) als voorbeeld. Fig. 1 Zoals je ziet hebben we nu op de stack 2 elementen staan: de variabele x (met de waarde 42 op de stack) en een pointer (een reference) naar c, waarvan de data op de heap staat. Als we nu een kopie van beiden maken, zoals in listing 2, ziet het geheugen er uit als in figuur 2. public struct PersoonData public int Leeftijd; public bool IsVrouw; Listing 3 Als je een variabele van het type PersoonData aanmaakt, komen er op de stack twee variabelen te staan: de integer Leeftijd en de boolean IsVrouw. Als je een kopie maakt van de variabele, kopieert de CLR dus twee variabelen. Neem nu eens de volgende listing 4: public void MijnMethod() int x = 42; MijnClass c = new MijnClass(); c.getal = x; int y = x; MijnClass d = c; Listing 2 Er is nu een tweede reference bijgekomen die echter naar hetzelfde stuk geheugen op de heap wijzigt. Dat houdt dus in dat als we c.getal veranderen dat dat ook gebeurt bij d.getal. Immers, die wijzen naar dezelfde integer op de heap. Als we echter X aanpassen heeft dat geen enkel effect op Y. Er is een kopie van de waarde gemaakt. Alles wat op de stack staat zal erg snel niet meer relevant zijn en dus opgeruimd worden. Een voorbeeld is een integer die je als variabele in je method declareert: na de method is deze int niet meer van belang public struct DommeStruct public Int64 a, b, c, d, e, f, g, h, i, j, k, l, m; Listing 4 Deze neemt op de stack 13 maal 8 bytes (long is een Int64, dus 8 bytes), dus 104 bytes in beslag. Op zich is dat niet zo n probleem, maar wat als je nu het volgende doet? DommeStruct x; x.a = 1; x.b = 2; DommeStruct y = x; DommeStruct z = y; Listing 5 Iedere keer als je een kopie maakt van de variabele, maakt de runtime ruimte voor de struct en kopieert hij alle waardes. Als we nu z.a magazine voor software development 31

32 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES.NET C# aanpassen, heeft dat geen enkel effect op x.a. Het is immers een kopie. Maar dit soort constructies kosten wel erg veel ruimte en ook tijd. De stack is immers bedoeld voor kleine hoeveelheden geheugen die snel weer weggegooid kunnen worden; de stack is geoptimaliseerd voor kleine hoeveelheden geheugen. Grotere stukken kunnen beter op de heap geplaatst worden, dat scheelt in performance. De stelregel is dan ook dat je moet proberen een struct niet groter te laten worden dan 16 bytes De stelregel is dan ook dat je moet proberen een struct niet groter te laten worden dan 16 bytes. Als je meer dan dat nodig hebt, kun je er beter een class van maken. Daarnaast is er nog een regel (die vrijwel nooit opgevolgd wordt): structs zouden immutable moeten zijn. Dat wil zeggen dat als ze eenmaal een waarde gekregen hebben, deze niet meer gewijzigd mag worden. Wijzig je hem toch, dan moet de oude struct van de stack verwijderd worden en zal er een nieuwe struct (met de nieuwe waardes) op de stack geplaatst worden. Dit heeft tot gevolg dat structs veel simpeler kunnen zijn en dus ook veel sneller. Vergeet niet: structs moeten zoveel mogelijk geoptimaliseerd zijn voor snelheid en een kleine geheugen footprint! Even kijken of je het goed begrepen hebt. Neem de code in listing 6: class Program private static readonly SomeWeirdStruct sws = new SomeWeirdStruct(); static void Main(string[] args) Console.WriteLine( sws.telop() ); Console.WriteLine( sws.telop() ); Console.WriteLine( sws.telop() ); public struct SomeWeirdStruct private int X; public int TelOp() this.x = this.x + 1; return this.x; Listing 6 Wat is het resultaat? Het zal je misschien verbazen, maar het resultaat is: 1, 1 en 1. Waarom gebeurt dit? Het antwoord is eigenlijk simpel: we hebben een sws gedefinieerd en die gebruiken we in de Main. Echter, als we deze struct benaderen, krijgen we een kopie van de sws variabele die bij Program hoort. Dus iedere keer krijgen we een nieuwe variabele die, je raadt het al, keurig X op 0 instantieert. Dus TelOp() geeft iedere keer 1 terug. Hadden we van SomeWeirdStruct nu een class gemaakt (en de naam aangepast naar SomeWeirdClass) dan had er 1, 2, 3 gestaan. Dit soort effecten kan een enorm verwarrend zijn voor mensen die het verschil tussen structs en classes, of liever, tussen value types en reference types niet goed begrijpen. Let hier op als je structs in je code gebruikt. Nog even de regels voor structs samengevat: Een struct: is kleiner dan of gelijk aan 16 bytes is immutable kan niet afgeleid worden (of afgeleid zijn van andere structs) kan wel interfaces implementeren kan properties bevatten kan methods bevatten de waardes in de struct worden gekopieerd als je de variabele kopieert moeten snel zijn Als je aan één van bovenstaande punten niet kunt voldoen, maak dan een class aan in plaats van een struct. Maar als je een struct kunt gebruiken, doe het dan ook: het scheelt enorm in performance. Primitive Types Er zijn een aantal speciale value types beschikbaar in C#. Deze noemen we de primitive types. Je kent ze ongetwijfeld wel, het zijn de types die je iedere dag weer gebruikt. In onderstaande lijst staan ze genoemd. Naam Alias Bereik System.Boolean bool True of False System.Byte byte System.SByte sbyte System.Char char Een unicode karakter System.Decimal decimal System.Double double -1, e , e308 System.Single float -3,402823e38.. 3,402823e38 System.Int32 int System.UInt32 uint System.Int64 long System.UInt64 ulong System.Int16 short System.UInt16 ushort Wellicht vraag je je nu af: En hoe zit het met string? Goede vraag, het antwoord is: string is een alias voor System.String maar dit is een reference type. Strings staan niet op de stack maar op de heap. Dus die horen niet in bovenstaand rijtje thuis. De aliassen zijn een makkelijker manier om met de primitive types om te gaan. Onderstaande twee regels doen precies hetzelfde; als je de gecompileerde code bekijkt in ILDasm zul je geen verschil zien. Het grote verschil zit hem echter in de leesbaarheid. System.Int32 getal1 = new System.Int32(); getal1 = 42; int getal2 = 42; Listing 7 Zowel getal1 als getal2 worden beiden aangemaakt op de stack en krijgen beiden de waarde 42. Welke vind jij makkelijker om te lezen? Enums Enumerated types, of kortweg enums, zijn in het.net framework 32 CONFERENCE MAGAZINE EVENTS

33 .NET C# enorm krachtige types. In talen als C++ kennen we enums ook, maar daar zijn ze niet meer dan een mooie, symbolische naam voor een getal. In C# zijn het echter object georiënteerde types, die volledige strongly typed zijn. Enums zijn afgeleid van System.Enum, welke op zijn beurt komt van System.ValueType. Enums zijn dus value types en worden op de stack geplaatst. De waardes in de enum zijn standaard van het type Int32. Bekijk de volgende enum eens: enum Geslacht Onbekend, Vrouw, Man Listing 8 Voorbeeld: [Flags] enum KlantSoort Onbekend = 0, Nieuw = 1, ContantBetalend = 2, FaktuurBetalend = 4, CreditCardBetalend = 8 Listing 11 We kunnen nu dus zeggen: KlantSoort soort = KlantSoort.Nieuw KlantSoort.ContantBetalend. Verderop in het kassasysteem kunnen we dit controleren: De compiler behandelt dit alsof je de volgende pseudocode geschreven zou hebben: struct Geslacht : System.Enum public const Geslacht Onbekend = (Geslacht)0; public const Geslacht Vrouw = (Geslacht)1; public const Geslacht Man = (Geslacht)2; KlantSoort soort = KlantSoort.Nieuw KlantSoort.ContantBetal // kijk hoe deze klant moet betalen if ((soort & (KlantSoort.CreditCardBetalend KlantSoort.FaktuurBetalend))!=0) Console.WriteLine("op rekening"); else Console.WriteLine("kontant" ); public Int32 value ; Listing 9 Dit kun je niet compileren: de compiler staat ons niet toe om iets af te leiden van System.Enum. Maar dit is wel wat er onder water gebeurt: er wordt een aantal constantes gedefinieerd van het type Int32, welke door middel van de variabele value opgevraagd kunnen worden. Uiteraard kun je je enums ook zelf waardes geven, of zelfs van een ander type gebruik laten maken. Bekijk de volgende code eens: enum LeeftijdCategorie : ushort Baby = 0, Kleuter = 5, Puber = 12, Volwassene = 18 Listing 10 LeeftijdCategorie is nu intern een lijst van constanten van het type ushort (oftewel System.UInt16) en bevat de 4 waardes die we opgegeven hebben. Let op: je kunt niet zomaar alles als type opgeven. Stel dat we LeeftijdCategorie definiëren als enum LeeftijdCategorie: System.UInt16, dan krijgen we een compilatiefout. We mogen alleen de types byte, sbyte, short, ushort, int, uint, long en ulong gebruiken, en dan zijn alleen maar de aliassen daarvan toegestaan. Nu is dat gelukkig in de praktijk wel voldoende, maar de eerste keer dat je die fout in je compiler tegenkomt, sta je wel vreemd te kijken. Immers, een System.UInt16 is precies gelijk aan ushort. De reden daarvoor is me niet duidelijk, het is een vereiste van de compiler om de alias te gebruiken voor de enums. We doen het dus maar gewoon zo. Listing 12 Door middel van een simpele & en bewerking kunnen we kijken welke vlaggen gezet zijn, zodat we precies weten of deze klant op rekening mag betalen of dat hij direct contant af moet rekenen. Dus Structs zijn lichtgewicht, snel en makkelijk. Classes zijn langzamer, groter maar veelzijdiger. Enums zijn speciale structs met bijzondere eigenschappen. Kies de juiste constructie voor je type en je zult merken dat je applicaties sneller en beter leesbaarder worden. C# is een enorm veelzijdige taal, maar je moet er wel moeite voor doen om alle facetten onder de knie te krijgen. Maar als je die tijd en moeite neemt, zul je merken dat je code er enorm door verbetert. Dennis Vroegop Dennis Vroegop is software architect bij Detrio, waar hij onder andere verantwoordelijk is voor het begeleiden van.net projecten. Hij is een programmeur in hart en nieren en is een echt community mens. Momenteel is Dennis naast zijn normale werkzaamheden ook voorzitter van de Nederlandse.NET gebruikersgroep. Bit-flags zijn een speciaal soort enums. Deze hebben waardes die een veelvoud zijn van 2: op die manier kun je ze combineren en uitlezen met de & (and) en (or) operators. magazine voor software development 33

34 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES DELPHI Bob Swart Delphi 2009 Taaluitbreidingen: Generics en Anonymous Methods Delphi 2009 is op 25 augustus aangekondigd, en was vanaf maandag 8 september leverbaar (alhoewel mensen met een subscription minimaal een week en soms nog langer op hun upgrade moesten wachten). Wat betreft nieuwe features ging de meeste aandacht uit naar de Unicode ondersteuning, gevolgd door DataSnap, maar ook de andere taaluitbreidingen zoals Generics en Anonymous Methods zijn beslist de moeite waard. Probleem is een beetje dat de on-line help zoals gewoonlijk wat achterloopt, en dus zijn niet alle (on)mogelijkheden even goed gedocumenteerd. Maar ik hoop met dit artikel (en mijn sessie tijdens de afgelopen SDC) een beetje licht in de schemering te kunnen werpen. De syntax van Generics is grotendeels vergelijkbaar met Delphi for.net Generics Laten we beginnen met Generics, of Parameterized Types, zoals ze ook wel in Delphi worden genoemd. Deze vinden hun oorsprong eerlijk gezegd in.net, en waren vorig jaar beloofd als nieuwe feature toen het niet haalbaar bleek om Generics al toe te voegen aan Delphi 2007 for Win32. De syntax is grotendeels vergelijkbaar met die van de Delphi for.net syntax, met als uitzondering dat het.net Framework een aantal zaken regelt die onder Win32 niet mogelijk zijn (in.net kun je alles naar een object boxen, en bestaan er al standaard interfaces om values met elkaar te vergelijken). Een generic type definitie kan gebruikt worden om functionaliteit te presenteren waarbij je van te voren nog niet weet of je die voor integers, strings, floats, of de een-of-andere object class wilt laten gebruiken. Als syntax worden de vishaken gebruikt met een type aanduiding ertussen (meestal een T, maar je kan er alles voor gebruiken, behalve reserved keywords). Om een verzameling van elementen van het type T weer te geven, gerepresenteerd door een array van T, kunnen we het volgende schrijven: type Verzameling<T> = Array of T; We kunnen variabelen direct van het type Verzameling<T> declareren, waarbij we dan wel voor de T een daadwerkelijk type moeten aangeven (de instantie van het type parameter als het ware), bijvoorbeeld als volgt: type VerzamelingIntegers = Verzameling<integer>; var X: VerzamelingIntegers; Y: Verzameling<integer>; // kan natuurlijk ook Behalve de type parameter T kunnen we ook constraints toevoegen, die daarmee aangeven dat we bij de instantiatie van het type aan bepaalde voorwaarden moeten voldoen. Een constraint is ofwel een class type waar de T van afgeleid moet zijn, of een interface die de T moet implementeren. Dus om een verzameling van elementen van een type T weer te geven waarbij T ten minste een TComponent moet zijn, kunnen we de volgende syntax gebruiken: type Verzameling<T: TComponent> = Array of T; Het type Verzameling<integer> zal nu niet meer compileren, maar Verzameling<TComponent> wel, en dat geldt ook voor de elementen die dan minstens van type TComponent moeten zijn. We kunnen behalve TComponent ook TPersistent gebruiken, maar het is niet mogelijk om TObject als constraint op te geven. In plaats daarvan moeten we dan het keyword class gebruiken: type Verzameling<T: class> = Array of T; En dit zegt dat we een verzameling kunnen bouwen van elementen die class instanties zijn (en geen integer of strings bijvoorbeeld). Generic Stack Om eens een voorbeeld te geven van het gebruik van Generics is het noodzakelijk om ook functionaliteit toe te voegen. Alleen maar een 34 CONFERENCE MAGAZINE EVENTS

35 DELPHI Array van een type T is niet zo zinvol, maar als je er ook mee kunt manipuleren (of onderling vergelijken, zoals ik aan het eind van dit artikel ga doen), dan is het plotseling een stuk handiger. Een voor de hand liggend voorbeeld is de Generic TStack, die als volgt gedefinieerd kan worden: type TStack<AnyType> = class protected FCount: Word; FStack: Array of AnyType;//needs to grow when needed public constructor Create(Capacity: Word = 42); destructor Destroy; override; function Count: Integer; function Push(const item: AnyType): Word; function Peek: AnyType; function Pop: AnyType; Omdat ik een array gebruik om de elementen van type T in op te slaan, moeten we zorgen dat de stack groot genoeg is (en blijft). Als eerste voorziening heb ik de constructor een default argument gegeven die de stack een opbergruimte van maximaal 42 elementen geeft (waar in het begin nog niks in zit natuurlijk, alleen de maximale ruimte is vast gereserveerd). Als de elementen instanties van classes zijn, dan wordt hun destructor niet aangeroepen dit kan dus tot een geheugenlek leiden als je de stack niet hebt leeggemaakt voor je hem weggooit De destructor roept de SetLength weer aan om het array weer op te ruimen. Let op: als de elementen instanties van classes zijn, dan wordt hun destructor niet aangeroepen; dit kan dus tot een geheugenlek leiden als je de stack niet hebt leeggemaakt voor je hem weggooit. De TStack is dus niet de eigenaar van de items die je erin stopt (dat kun je er wel van maken, maar dat moet je bijvoorbeeld aangeven dat de elementen minstens classes zijn via de constraint syntax, en dan in de destructor eerst Free aanroepen van alle elementen voordat je de SetLength op het array aanroept maar dat laat ik over als oefening voor de lezer). constructor TStack<AnyType>.Create(Capacity: Word = 42); begin inherited Create; FCount := 0; SetLength(FStack, Capacity) destructor TStack<AnyType>.Destroy; begin SetLength(FStack,0); inherited De Push methode moet eerst kijken of het interne array van de stack wel groot genoeg is om het nieuwe element op te slaan. Zo niet, dan moet SetLength opnieuw worden aangeroepen om het array te laten groeien. Hiertoe kunnen we SetLength weer opnieuw aanroepen (die er tevens voor zorgt dat alle bestaande elementen uit het array meegenomen worden naar de nieuwe lengte). In de code vergroot ik het array met stapjes van 1 (door de nieuwe benodigde count mee te geven), maar het is wellicht slimmer om dit in iets grotere stappen te doen, afhankelijk van de behoefte aan extra opslagruimte van de stack. De implementatie van de Peek en Pop spreekt voor zich, neem ik aan. Merk op dat we hier steeds het AnyType gebruiken, waardoor de stack dus inderdaad van alles kan bevatten, van strings tot integers of TDataSets. function TStack<AnyType>.Count: Integer; begin Result := FCount function TStack<AnyType>.Push(const item: AnyType): Word; begin Inc(FCount); if FCount > Length(FStack) then SetLength(FStack, FCount); FStack[FCount-1] := item; Result := FCount function TStack<AnyType>.Peek: AnyType; begin Result := FStack[FCount-1] function TStack<AnyType>.Pop: AnyType; begin Result := Peek; Dec(FCount) We kunnen b.v de TStack gebruiken voor een lijst van strings die we in omgekeerde volgorde weer willen printen, en wel als volgt: type TStringStack = TStack<string>; var S: TStringStack; begin S := TStringStack.Create; try S.Push('Delphi 2009'); S.Push('C++Builder 2009'); writeln(s.pop); writeln(s.pop); finally S.Free end. Of we gebruiken hem als een stack van TPersistent classes, waarbij we bijvoorbeeld de Name en de ClassName maar ook de UnitName (ook een nieuwe property in Delphi 2009) kunnen laten zien: procedure TFormMain.ButtonClick(Sender: TObject); var CompStack: TStack<TPersistent>; Comp: TComponent; begin CompStack := TStack<TPersistent>.Create; try for Comp in Self do CompStack.Push(Comp); magazine voor software development 35

36 Advertentie Microsoft

37 Advertentie Microsoft

38 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES DELPHI while CompStack.Count > 0 do begin Comp := CompStack.Pop as TComponent; ShowMessage(Comp.Name + ': ' + Comp.ClassName + ' (' + Comp.UnitName + ')') end finally CompStack.Free end Voor dit voorbeeld is het maar goed dat de stack niet zijn elementen allemaal free t, want anders zou het Form plotseling leeg zijn nadat we deze code hebben uitgevoerd (leuk voor een demo, maar niet leuk in praktijk). Overigens zijn er al veel Generic types te vinden in de unit Generics.Collections, dus moeten we opletten niet het wiel opnieuw uit te vinden. Generic Methods Behalve Generic Types maak ik zelf ook veel gebruik van Generic Methods. Het enige jammere is dat ik hier geen gewone lokale routines voor kan gebruiken, maar dat in de vorm van class methods moet doen. We kunnen hierbij de type parameter op twee plaatsen toevoegen: of bij de class of bij de method zelf. In de volgende code snippet staan beide alternatieven naast elkaar: type TGeneric<AnyType> = class class procedure Swap(var X,Y: AnyType); static; type TGeneric = class class procedure Swap<AnyType>(var X,Y: AnyType); static; Als het maar om één method gaat is het verschil niet zo groot, maar als er meerdere methods worden toegevoegd zul je al snel merken dat het flexibeler is om de type parameter toe te voegen aan de method en niet aan de class. Zeker als er methods komen die wellicht meer dan één type parameter hebben. De implementatie van de Generic Swap methode zelf is redelijk eenvoudig: een derde variabele van type AnyType en klaar is kees: class procedure TGeneric.Swap<AnyType>(var X,Y:AnyType); var Z: AnyType; begin Z := X; X := Y; Y := Z Bij het aanroepen hoeven we natuurlijk geen instantie van TGeneric aan te maken, maar kunnen we direct de class function Swap aanroepen, met daarbij een specificatie voor de type parameter en de juiste argumenten. Voor een tweetal integer variabelen kunnen we dit als volgt doen: Op dezelfde manier kunnen we andere class methods bouwen zoals een IFF (afhankelijk van een expressie krijg je de TrueValue of the FalseValue terug) of een ChooseDef (kies een item uit een lijst, maar als de index buiten het bereik van de lijst is krijg je een default waarde terug). type TGeneric = class class function IFF<AnyType>( const Expression: Boolean; TrueValue: AnyType; FalseValue: AnyType): AnyType; class function ChooseDef<AnyType>(index: Integer; const values: Array of AnyType; default: AnyType): AnyType; Hier zien we een verschil met het.net Framework, waar de default waarde van een type door het framework zelf wordt gegeven. Dat bestaat in Win32 niet, vandaar dat ik de default waarde van het type AnyType als extra argument aan de generic method heb toegevoegd, en we hem bij iedere aanroep moeten meegeven. Niet zo elegant als de.net implementatie, maar het werkt net zo goed. De implementatie van beide generic methods is als volgt: class function TGeneric.IFF<AnyType>( const Expression: Boolean; TrueValue, FalseValue: AnyType): AnyType; begin if Expression then Result := TrueValue else Result := FalseValue class function TGeneric.ChooseDef<AnyType>( index: Integer; const values: array of AnyType; default: AnyType): AnyType; begin if (index >= Low(values)) and (index <= High(values)) then Result := values[index] else Result := default Voor ik verder ga met een laatste Generic Method (om het grootste of kleinste element van een lijst van type AnyType te vinden) wil ik eerst even een uitstapje maken naar een noodzakelijk bouwsteen daarvoor: de Anonymous Methods. Anonymous Methods Een Anonymous method is een methode zonder naam, maar wel een stuk code binnen een procedure of function sectie. Zoiets als een stuk code dat je via copy-en-paste ergens hebt neergezet maar met een wrapper eromheen. Ze zijn zeker niet overal even goed toepasbaar, en leiden ook niet altijd tot leesbare code, maar in sommige situaties kunnen ze een hulpmiddel zijn om de expressiemogelijkheden van de taal Delphi te vergroten. Aan de hand van een eerste eenvoudig voorbeeld zal ik tot slot een tweetal nuttige toepassingen van Anonymous Methods laten zien. Een Anonymous Method geven we aan met de keywords reference to procedure (of function), inclusief de parameterlijst. Het invullen gaat dan in een blok code, waarbij er geen puntkomma komt tussen de header en de eerste begin. var A,B: Integer; begin A := 42; B := 17; TGeneric.Swap<integer>(A,B); type TProc = reference to procedure(x: Integer); procedure call(const proc: TProc); begin proc(42); 38 CONFERENCE MAGAZINE EVENTS

39 DELPHI var TheAnonymousMethod: TProc; begin TheAnonymousMethod := procedure(a: Integer) begin Button1.Caption := IntToStr(a) // einde van de Anonymous Method call(theanonymousmethod) De assignment van het stukje code aan de variabele TheAnonymousMethod ziet er op het eerste gezicht niet zo leesbaar uit, vooral niet als het stuk code lang(er) wordt. Er zijn echter wel voordelen te halen uit het gebruik van Anonymous Methods, zoals ik in de volgende voorbeelden zal laten zien. Een van de plaatsen waar Anonymous Methods van waarde kunnen zijn, is binnen de synchronize methode van een TThread component Nuttige Anonymous Methods Een van de plaatsen waar Anonymous Methods van waarde kunnen zijn, is binnen de synchronize methode van een TThread component. Dit zou anders een extra methode kosten, inclusief velden voor de parameters van deze methode. Dit is o.a. in meer detail besproken in de weblog van Allen Bauer (zie /2008/ 09/08/38868), waar hij spreekt over het thrddemo project in de C:\Documents and Settings\All Users\Documents\RAD Studio\6.0\Demos\ DelphiWin32\VCLWin32\Threads directory. Grappig genoeg is de daadwerkelijke demo niet aangepast volgens zijn suggesties, dus kunnen we dat zelf uitproberen. De oorspronkelijke code is terug te vinden in bovenstaande locatie, maar de aangepaste versie met Anonymous Methods zou er als volgt uit komen te zien: procedure TSortThread.VisualSwap(A, B, I, J: Integer); begin $IFDEF ANONYMOUS Synchronize(procedure begin with FBox do begin Canvas.Pen.Color := clbtnface; PaintLine(Canvas, I, A); PaintLine(Canvas, J, B); Canvas.Pen.Color := clred; PaintLine(Canvas, I, B); PaintLine(Canvas, J, A) end end) $ELSE FA := A; FB := B; FI := I; FJ := J; Synchronize(DoVisualSwap); $ENDIF De $IFDEF ANONYMOUS kan gebruikt worden om te switchen tussen de oorspronkelijke code (die de extra methode DoVisualSwap aanroept) en de aanroep van de Anonymous Method die de parameters A, B, I en J direct ontvangt. Een tweede voorbeeld van een nuttig gebruik van Anonymous Methods heb ik gevonden bij het maken van generic methods voor het bepalen van het grootste of kleinste element uit een lijst van items van een bepaald type. Omdat hier de vergelijking tussen twee elementen cruciaal is, kunnen we dit niet zomaar implementeren. Daar hebben we iets extra s voor nodig, wat we o.a. terugvinden in de Generics.Defaults unit. Generics.Defaults Delphi 2009 bevat de units Generics.Defaults en Generics.Collections die al een hoop voorgedefinieerde nuttige voorbeelden van Generics (en Anonymous Methods) bevatten. Zo bevat Generics.Defaults de definitie van het IComparer<T> interface dat we straks willen gebruiken, en ook een TComparison<T> Anonymous Method kunnen we hier terugvinden: type IComparer<T> = interface function Compare(const Left, Right: T): Integer; TComparison<T> = reference to function( const Left, Right: T): Integer; Deze Anonymous Method kan o.a. gebruikt worden in de aanroep van de Construct methode van de TComparer<T> class, die het IComparer<T> interface implementeert. TComparer<T> = class(tinterfacedobject, IComparer<T>) public class function Default: IComparer<T>; class function Construct(const Comparison: TComparison<T>): IComparer<T>; function Compare(const Left, Right: T): Integer; virtual; abstract; Daarnaast bevat de Generics.Defaults unit voorbeelden om de gelijkheid van generic values te bepalen, en een niet-reference counted IInterface implementatie in het type TSingletonImplementation, inclusief de implementaties van QueryInterface, _AddRef en _Release. Zie de source code voor details. Combinatie van Anonymous Methods en Generics Het laatste voorbeeld is er eentje dat ik zelf regelmatig gebruik: een generic method die de grootste of kleinste waarde uit een lijst van values van een bepaald type teruggeeft. Vergelijkbaar met de ChooseDef, maar dan inclusief een vergelijking zoals we die in het IComparer interface uit de Generics.Defaults unit zagen. De definitie van mijn functions Min en Max is als volgt: type TGeneric = class class function Min<AnyType>(const values: array of AnyType; const Comparer: IComparer<AnyType>): AnyType; class function Max<AnyType>(const values: array of AnyType; const Comparer: IComparer<AnyType>): AnyType; Als eerste argument geef ik de lijst van values van type AnyType mee, en als tweede argument het IComparer interface van hetzelfde type. Hoe we aan deze IComparer komen is nog even niet van belang dat wordt tijdens de aanroep geregeld (met een Anonymous Method). Wat we eerst moeten bekijken is de implementatie van de Min en Max functies, waarbij we de Compare functie van het IComparer interface kunnen gebruiken om steeds twee values van het AnyType te vergelijken met elkaar. De Compare geeft een positief getal terug als links groter is dan rechts, en anders een negatief getal. In feite kun je het magazine voor software development 39

40 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES DELPHI zien als links min rechts als het getallen zouden zijn. class function TGeneric.Max<AnyType>(const values: array of AnyType; const Comparer: IComparer<AnyType>): AnyType; var item: AnyType; begin if length(values) >= 1 then begin Result := values[low(values)]; for item in values do if Comparer.Compare(item,Result) > 0 then Result := item end class function TGeneric.Min<AnyType>(const values: array of AnyType; const Comparer: IComparer<AnyType>): AnyType; var item: AnyType; begin if length(values) >= 1 then begin Result := values[low(values)]; for item in values do if Comparer.Compare(item,Result) < 0 then Result := item end Tot nu toe is de code nog goed te lezen en niet al te vreemd gezien de generic methods die ik eerder liet zien. De slagroom op de taart krijgen we wanneer we de Min of Max willen aanroepen, bijvoorbeeld door het grootste getal uit een dynamisch array van integers op te leveren. Het eerste deel van de aanroep van Max zou er als volgt uit kunnen zien: TGeneric.Max<integer>([1,2,4,8,16,32,64,42,36,13], Voor het tweede argument hebben we dan iets nodig dat het IComparer interface oplevert. En daarvoor moeten we even terugdenken aan de Generics.Defaults unit, met daarin de TComparer class met de Construct class function die het IComparer interface teruggeeft. Omdat de Construct method een class function is, kunnen we gewoon TComparer<integer>.Construct aanroepen om een IComparer<integer> terug te krijgen. Moeten we alleen nog het Comparison argument van de Construct meegeven, en dat is een TComparison<T> oftewel een reference to function(const Left, Right: T): Integer; Om een lang verhaal kort te maken: bij de aanroep van Construct kunnen we dus meteen een Anonymous Method schrijven en die meegeven als argument, zoals in de volgende code te zien is: TGeneric.Max<integer>([1,2,4,8,16,32,64,42,36,13], TComparer<integer>.Construct( function(const Left,Right: integer): integer begin Result := Left-Right end)) De Anonymous Method doet in feite niets anders dan Rechts van Links aftrekken om zo het gewenste gedrag te implementeren dat binnen de Max functie verwacht wordt. Tijdens de aanroep van TGeneric.Max<integer> geef ik dus behalve het type en de lijst van elementen van dat type ook meteen een dynamische vergelijk-functie mee voor elementen van dat type (in de vorm van een Anonymous Method). En dat moet je misschien twee of drie keer lezen voor het duidelijk wordt, maar werkt wel perfect. Als variatie op dit thema wil ik nog een laatste voorbeeld geven waarbij ik van een lijst van componenten de grootste wil hebben, en ik grootste definieer als het component met de langste string die de nieuwe ToString functie teruggeeft. Hiermee geef ik meteen aan dat ik van aanroep tot aanroep de implementatie van de vergelijking tussen twee elementen kan aanpassen door een andere Anonymous Method te schrijven. Dit voorbeeld ziet er uiteindelijk als volgt uit, waarbij ik eerst een array van TComponents maak door de Self.Components van een TForm af te lopen: procedure TFormX.ButtonClick(Sender: TObject); var SelfComponents: array of TComponent; i: Integer; begin SetLength(SelfComponents, Self.ComponentCount); for i:=0 to Self.ComponentCount-1 do SelfComponents[i] := Self.Components[i]; ShowMessage( TGeneric.Max<TComponent>(SelfComponents, TComparer<TComponent>.Construct( function(const Left,Right: TComponent): integer begin Result := Length(Left.ToString) Length(Right.ToString) end)).tostring ) Met deze code in de OnClick van een TButton op een form krijg je als resultaat de inhoud van het component met de langste ToString waarde. Maar natuurlijk is het nu niet moeilijk meer om een eigen criterium te implementeren, of dit principe te gebruiken voor andere Generics en/of Anonymous Methods. Conclusie In dit artikel heb ik laten zien hoe de nieuwe Delphi 2009 taalelementen voor Generic Types en Generic Methods alsmede Anonymous Methods in elkaar zitten en hoe we die zelf kunnen maken en gebruiken. Vooral de combinatie van Generics en Anonymous Methods levert fijne nieuwe mogelijkheden die voorheen niet aan wezig waren zonder dit allemaal als vele losse routines uit te schrijven. Wie nog vragen of opmerkingen heeft kan me altijd per bereiken of een bezoek brengen aan mijn nieuwe trainingsruimte in Helmond Brandevoort (zie voor details) 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. 40 CONFERENCE MAGAZINE EVENTS

41 Advertentie Avanade

42 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES DOT NET NUKE Nick Boumans Portaltechnologie: SharePoint of DotNetNuke? De keuze voor portaltechnologie is vaak snel gemaakt. De keuze of men gebruik gaat maken van Microsoft SharePoint of DotNet- Nuke (DNN) ligt vaak minder voor de hand. Is de keuze eenmaal gemaakt, dan twijfelt men later vaak of wellicht toch het andere product het voordeel van de twijfel had moeten krijgen. Of men kiest deels uit onwetendheid: men kent of SharePoint of DNN. Als SharePoint specialist heb ik recent kennis gemaakt met de mogelijkheden van DotNetNuke. De kennis die ik hierbij heb opgedaan is voor mij van cruciale waarde om keuzes in de toekomst beter te verantwoorden. In dit artikel zal een aantal overeenkomsten c.q. verschillen van beide producten in kaart worden gebracht. Doel hiervan is het verbreden van uw inzichten in deze producten die u kunnen helpen bij het beantwoorden van de veelgestelde vraag: SharePoint of DotNetNuke? Een Portal is een Gateway waarmee leden toegang hebben tot bedrijfsinformatie SQL Server Database Beide producten maken gebruik van intuïtieve Microsoft technologie en beide gebruiken SQL Server als database. DotNetNuke kan overigens ook op een andere database werken, maar alleen SQL Server wordt standaard ondersteund. Tevens kunnen zowel SharePoint als DNN uitgebreid worden door gebruik te maken van.net-development. Zowel SharePoint als DotNetNuke gebruiken dus SQL Server voor de opslag van data. Een belangrijk verschil is echter dat we de Share- Point contentdatabase enkel en alleen mogen benaderen via het SharePoint interface (een site) of via het objectmodel (door te programmeren). DotNetNuke heeft een datamodel dat eventueel direct benaderbaar is. Zo kan men vrij snel in DotNetNuke direct in de database een portal-url aanpassen. In SharePoint is dit helaas niet mogelijk. Tevens is het met beide producten mogelijk om door middel van maatwerk gebruik te maken van een andere database. Dit hoeft dan ook niet per definitie SQL Server te zijn. Deze extra database kan echter niet gebruikt worden als vervanging voor de installatie database! Central Admin en Host Account SharePoint gebruikt de Central Administration voor Farm-level beheertaken. De Central Admin is een aparte webapplicatie die apart te benaderen is. DotnetNuke kent het Host -account waarmee extra privileges worden verkregen na het authenticatieproces. Inloggen kan echter direct op de portal. Fig. 1: SharePoint Central Administration Toevoegen van Custom WebParts en Modules SharePoint kent een tool die Stsadm.exe heet. Met deze commandline tool is het o.a. mogelijk om custom WebParts te deployen en beschikbaar te maken op een website. Dergelijke deployment gebeurt doorgaans middels een Windows Solution Package (.WSP). Een solution package is een gecomprimeerd bestand met daarin de functionaliteiten. Een of meer XML bestanden beschrijven waar de bestanden uit de.wsp file geplaatst dienen te worden. Het maken en deployen van een solution file vereist redelijke SharePoint kennis. Deployment is voor veel developers en bedrijven dan ook een bottleneck. De solution package dient gedeployed te worden naar de webserver(s). Bij gebruik van stsadm.exe kan dit niet gedaan worden via de webbrowser. In een organisatie betekent dit vaak het inschakelen van een beheerder met voldoende rechten. Modules voor DotNetNuke kunnen via de webbrowser worden toegevoegd. Voor het bouwen hiervan is uiteraard DNN-kennis vereist. Het packagen van een module (.zip-file) vraagt over het algemeen echter minder expertise dan het maken van een Solution Package voor SharePoint. Het nadeel van DotNetNuke ten opzichte van SharePoint op dit vlak is, dat DNN-modules alleen binnen DNN gebruikt kunnen worden. SharePoint WebParts kunnen gebruik maken van twee base classes: SharePoint WebPart of ASP.NET WebPart. WebParts gebaseerd op de ASP.NET base class kunnen ook in ASP.NET-sites worden gebruikt. SharePoint specifieke WebParts kunnen gebruikt worden in systemen die gebruik maken van Windows SharePoint Services. Hierbij kan men 42 CONFERENCE MAGAZINE EVENTS

43 DOT NET NUKE bijvoorbeeld denken aan Project Server. Een ASP.NET WebPart is weliswaar niet direct in DotNetNuke te gebruiken, het is wel mogelijk een module te maken waarin een WebPart wordt gehost. Fig. 2: DNN - Toevoegen van een module Visibility In SharePoint kan men gebruik maken van Audience Targeting en Security Features om bepaalde data zichtbaar te maken of juist te verbergen. Vaak komt het voor (vooral in WSS) dat de gebruiker meer te zien krijgt dan hij of zij eigenlijk mag zien. Bij DNN is het mogelijk om instanties van modules (dus een module die op een pagina is geplaatst) te koppelen aan één of meer rollen. Per rol kan worden aangegeven welke rechten (View, Edit, ) van toepassing zijn op de betreffende module. Standaard worden de rechten op een module geërfd van de pagina, zodat gebruikers die de pagina mogen zien, ook de module mogen zien. Daarnaast kunnen aan gebruikers individuele rechten worden toegekend. Deze rechten komen dan wel altijd bovenop de rechten die de gebruiker vanuit de aan hem toegekende rollen heeft. Scalability DNN kan geïnstalleerd worden op één webserver en één SQL Server. Wanneer men DotNetNuke in een webfarm of garden zouden willen laten draaien, dient men daarvoor een en ander handmatig te configureren. Concreet heeft iedere DNN-installatie dus één enkele DNN-database. Wel is het mogelijk DNN meerdere malen te installeren op een server. Dit in tegenstelling tot SharePoint. SharePoint is echt een serverproduct waarvan maar één instantie op een server geïnstalleerd kan worden. Het beste kan men SharePoint dan ook zien als een serverproduct en DNN als een ASP.NET applicatie. In SharePoint is het mogelijk de scalability te beheren vanuit de Central Administration. Het is zelfs mogelijk om bijvoorbeeld per Site Collection een aparte contentdatabase te hebben. Tevens kent SharePoint Features als Enterprise Search en Single Sign-on. We kunnen zelfs gebruik maken van een aparte Search en Indexing server. Hierbij kan men gebruik maken van uitgebreide indexeeropties. DotNetNuke stelt hier tegenover dat veel performance gerelateerde instellingen via het Host-menu kunnen worden geconfigureerd en dat standaard onderdelen zoals de zoek- en indexeermogelijkheden met maatwerk of software van derden kunnen worden aangepast. Office-Integratie en Multi-Language SharePoint is heel sterk in Office integratie (MOSS 2007: Microsoft Office SharePoint Server 2007). Bij DotNetNuke ontbreekt dit eigenlijk. Zo is het met Infopath Forms Services mogelijk om in een handomdraai een professioneel formulier te maken wat ingevuld kan worden door de gebruiker. De data kan opgeslagen worden in o.a. een SharePoint lijst. Een ander voorbeeld is integratie met Outlook. Bij gebruik maken van een workflow kan een taak gecreëerd worden in een takenlijst. Deze taak kan zichtbaar worden in de Outlook-takenlijkst van de gebruiker. manier. Bij SharePoint is dit niet standaard. Het is uiteraard wel mogelijk om meerdere talen te installeren. Doorlooptijd en kosten Over het algemeen is de tijdsduur voor het up-and-running krijgen van een out-of-the-box DotNetNuke portal (installatie en configuratie) korter dan die van SharePoint. Dit geldt niet alleen voor interne hosting maar ook voor externe hosting. Wil men bij SharePoint eveneens toegang hebben tot de stsadm.exe tool dan zal men eveneens remote desktop toegang moeten hebben tot de webserver. Aangezien DotNetNuke Open Source is, en een zeer liberaal licentiemodel kent, vallen de kosten van DotNetNuke al snel lager uit. Sharepoint heeft ook de WSS variant die gratis verkrijgbaar is bij Windows 2003, maar deze is enigszins beperkt in functionaliteit ten opzichte van de grote broer MOSS Het is echter te kort door de bocht om te stellen dat DotNetNuke altijd goedkoper is. Deze afweging kan alleen per situatie apart worden gemaakt. Intranet v.s. extranet Met beide pakketten is het mogelijk om zowel een intranet- als internetportal op te zetten. Men kan hierbij gebruik maken van verschillende authenticatiemechanismen. Of de oplossing een internet- of intranetapplicatie moet worden kan een belangrijke factor zijn bij de bepaling van de keuze voor DotNetNuke of SharePoint. Praktijk: WebPart development vs. Module development In de volgende paragrafen zal dieper ingegaan worden op het development aspect van een WebPart c.q. Module. Hierbij is het nadrukkelijk niet de bedoeling om een complete tutorial te beschrijven voor de development hiervan, maar wil ik alleen enkele verschillen of overeenkomsten schetsen. Tevens dient opgemerkt te worden dat zowel bij WebPart- als Module development meerdere wegen bestaan die naar Rome leiden. WebParts en Modules voegen functionaliteit toe om te voorzien in de bedrijfsbehoefte SharePoint: WebPart Development De eenvoudigste manier om een WebPart te ontwikkelen is door gebruik te maken van WSS extensions for Visual Studio. Deze extensies voor Visual Studio zijn zowel voor Visual Studio 2005 als 2008 beschikbaar als download op de site van Microsoft. Na het installeren van deze extensies hebben we binnen Visual Studio de beschikbaarheid over een aantal extra project templates. Een hiervan is het SharePoint WebPart. Hier staat tegenover dat Multi-Language standaard is bij DotNetNuke. Dit is al sinds begin 2005 geïmplementeerd op de standaard ASP.NET SharePoint is heel sterk in Office integratie; hier staat tegenover dat Multi- Language standaard is bij DotNetNuke Fig. 3: De beschikbare templates in VS2008 na installatie van WSS Extensions for Visual Studio magazine voor software development 43

44 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES DOT NET NUKE Na de keuze voor WebPart ziet ons Visual Studio project er als volgt uit: Bij de referenties in het project kan men zien dat een referentie is toegevoegd naar de Microsoft.Share- Point.dll. Dit is slechts een van de vele dll s die gebruikt kunnen worden voor SharePoint development. SharePoint objecten beginnen met SP (SharePoint). Een lijst heet dus binnen SharePoint een SPList, dit in vergelijking met een generic list. Door de toevoeging van SP voor een objectnaam zijn objecten voor.net developers eenvoudiger te herkennen. Door een naamconventiefout zorgen de SPSite- en SPWeb objecten echter vaak voor verwarring: SPSite geeft toegang tot de top-level site (dit is een collection) SPWeb geeft toegang tot de pagina (hier zou men wellicht SPSite verwachten) Binnen een webpart kan men op de volgende manier toegang krijgen tot de pagina (SPWeb): SPSite sitecollection = SPContext.Current.Site; SPWeb site = SPContext.Current.Web; Listing 1: Het benaderen van een webpagina (SPWeb) vanuit een WebPart Door in de Project Properties de juiste url op te geven bij Start Browser With Url kan met één druk op F5 gedeployed worden naar een development server. Als het goed is, is het WebPart nu beschikbaar in de WebPart Gallery en klaar voor gebruik. Het maken van een uitgebreider Solution Package en deployment binnen bijvoorbeeld een OTAP omgeving vergt uiteraard meer kennis en kunde dan enkel en alleen een druk op F5. Debuggen is mogelijk door de debugger te attachen aan het juiste W3WP proces. Naast de WSS Extensions gebruiken developers vaak tools van CodePlex om WebParts en Windows Solution Files te maken. Veel gebruikte tools zijn: STSDEV, WSPBuilder en SharePoint Solution Installer. DotNetNuke: Module Development Om een DotNetNuke Module te ontwikkelen kan men als vergelijkbare stap met de WSS Extensions for Visual Studio gebruik maken van de starterkit van DotNetNuke. Deze bevat eveneens een aantal project templates. Hiermee is een ontwikkelaar snel op het juiste spoor gezet. Aangezien DotNetNuke gewoon een ASP.NET applicatie is, kan het maatwerk worden gedebugged vanuit Visual Studio en zijn er op de ontwikkelmachine geen extra installaties nodig. Met DotNetNuke kan een ontwikkelaar al aan de slag met louter de (gratis) Express-edition producten van Microsoft. Fig. 5: Bij DotNetNuke worden diverse projecttemplates meegeleverd in de starterkit Conclusie Aangezien iedere oplossing andere requirements heeft, zal steeds overwogen moeten worden voor welk product we kiezen. Veel mensen neigen bij bijvoorbeeld een webshop meer naar DotNetNuke, terwijl bij een enterprise intranet, waarbij Office-integratie een grote rol speelt, SharePoint de voorkeur verdient. Scalability kan een belangrijk kwaliteitsaspect zijn. Door de huidige situatie te bekijken waar de applicatie aan dient te voldoen en wat de verwachting is over 5 jaar (bijv. omvang gebruikers- en site-toename) kan men een goed beeld krijgen van de vereiste scalability. Hierbij dient men te voorkomen dat de huidige keuze DNN is en over 5 jaar SharePoint (of vice versa). Een valkuil die men vaak ziet is dat een definitieve keuze gemaakt is voor DotNetNuke of SharePoint binnen een organisatie en dat men met dit product alle problemen probeert op te lossen. Heel geforceerd tracht men oude applicaties in dit product te verweven. Uiteraard kan het allemaal wel, maar men moet zich wellicht de vraag stellen: is dit de beste oplossing voor deze situatie?. Links: Blog Nick Boumans: WSS Extensions for Visual Studio: downloads/details.aspx?familyid=7bf65b28-06e2-4e87-9bad- 086e32185e68&displaylang=en Microsoft Office SharePoint Server 2007 VHD: microsoft.com/downloads/details.aspx?familyid=67f93dcbada8-4db5-a47b-df17e14b2c74&displaylang=en Nick Boumans Nick Boumans is MOSS 2007 Technology Specialist en Enterprise Application Developer bij Giraffe IT. Een belangrijke keuze bij het maken is of men een module wenst te maken die gecompileerde bestanden bevat of een dynamic module. Bij een dynamic module worden de bronbestanden als het ware gecompileerd op de server: dat laten we dan over aan ASP.NET. In tegenstelling tot een WebPart die meestal als Windows Solution File gedeployed zal worden, is de module file een.zip-bestand. Vanuit onze DNN development omgeving kunnen we deze.zip-file laten genereren inclusief een configuratiefile. Hierbij is er de keuze voor het maken van een module met of zonder bronbestanden. Het gecrëeerde zipbestand kan vervolgens direct toegevoegd worden aan een DotNet- Nuke site. 44 CONFERENCE MAGAZINE EVENTS

45 CORE SYSTEMS Rolf Craenen Core Systems Wat hebben je hypotheek, een pinautomaat, de logistiek in de Rotterdamse Haven en (sorry) ook je belastingaangifte met elkaar gemeen? Ze draaien allemaal op kernsystemen in bewezen technologieën die vaak al tientallen jaren in de lucht zijn en bekend staan om hun performance en stabiliteit. In programmeertalen zoals COBOL, Uniface, NonStop, PL1 en C zijn applicaties ontwikkeld die bedrijfskritische processen ondersteunen van grote bedrijven en overheidsorganisaties. Het SDN gaat aandacht besteden aan software development van dit type applicaties in een nieuwe track met als naam Core Systems. Hiermee verbreden we de dekking van het SDN op het gebied van systeemkarakteristieken, gebruikte programmeertalen, databases en ontwikkelhulpmiddelen en hun leveranciers. Als je kijkt naar de karakteristieken van core systems, zie je dat er naast grote overlap met andere vormen van software development ook duidelijk verschillen zijn. Aan de ene kant zijn performance en stabiliteit sterke punten maar tegelijkertijd zijn de systemen minder flexibel en zijn developers schaars. Ook blijkt uit onderzoek dat een mainframe een factor 10 efficiënter met energie omgaat dan gedistribueerde systemen. Dit gegeven was recent voor bijvoorbeeld voor Duitse Postbank een reden om 3 mainframes aan te schaffen vanwege de aansluiting met hun groene it-beleid. Uiteraard worden Core Systems ontwikkeld met software die al sinds jaar en dag hun plek hebben binnen het SDN. Met name door de wens om gegevens, reeds beschikbaar binnen de Core Systems, te ontsluiten richting internet. Tegelijkertijd kom je technieken tegen als Cobol, C, NonStop, IDMS, PL/1, DB2, CICS, TSO en ISPF. Bedrijven zoals IBM, HP en Compuware zijn al decennia belangrijke spelers op dit gebied. Binnen deze bedrijven bestaat veel enthousiasme om ook hun kijk op het Engineering-vakmanschap te delen binnen het SDN. Elders vind je het eerste artikel met als titel The best of both worlds, Modernizing Core Sytems. Op het SDN event van 12 december verzorgen wij 5 presentaties. Wij nodigen je van harte uit om kennis te komen maken. Heb je ideeën, wil je meedoen of meer weten, stuur dan een aan rolf.craenen@sdn.nl. Rolf Craenen Advertentie Qurius

46 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES GENERAL Interesting Things: Increasing speed Early October I presented a talk at the inspiring Software Development Conference 2008 in Noordwijkerhout, the Netherlands. My talk had an inspiring, or at least intriguing title: How to keep our jobs. Why? Well, let me share some thoughts on the productivity of software development projects. A 100-fold Recent research by Gartner shows that the productivity of software development will need to increase by a 100-fold. According to Gartner, there are 2 very simple and straightforward reasons for this: Not enough newbies. The number of university graduates, and other people starting new with software development is too limited. Increasing demand. The demand for new software is ever increasing, for instance because of emerging technologies and platforms (think of mobile and web 2.0). And from my personal experience I would add: Poor quality software. I perform a lot of code audits on applications, often quite large, let s say over lines of code. Even with such large applications, built by respectable companies, the quality is often brain damaging poor, frequently due to failing or total lack of software architecture. Such software is therefore very hard to maintain or extend with new features. Ongoing business. There is a vast amount of software present in the marketplace that will need to be maintained for a very long time. Large transactional systems with banks (nationalized or not), governmental organizations or packaged software such as SAP and PeopleSoft. Although not mind-blowing, this work still needs to be done, and limits the amount of resources available for new development. No silver bullet. There is no single methodology, technology or company that is able to provide the productivity to meet up the Gartner challenge, no matter how hard vendors will try to convince you that their new tool or framework will get you there. Complexity of technology. Technology is ever more increasing in complexity. As much as you might suggest that tools and technologies have evolved over the past 20 years - about as long as I have been in this business - we do not reach higher productivity in projects than we did back then during the client-server era. In good old PowerBuilder projects we reached productivities of around 2.5 hours per function point. Nowadays, when applying our full blown agile Accelerated Delivery Platform we can about get to the same level, as was recently calculated by a respectable customer. And this is 20 years later. Another quick example? Take service oriented architecture. Often presented by vendors and architects as an approach where you just model the business processes and out comes your new software. Yeah right. Building service oriented software is still hard and technologically complex work. And no matter who gave you the idea that service orientation will help you get rid of object orientation, don't believe it. You will need good old object technology to build all this stuff architects come up with: user interfaces, process logic, business logic, interfaces to middleware. It's just not that simple. Band wagons. We developers tend to go along with every new tool, framework or language extension that comes of the Microsoft, Sun, IBM, Oracle or open source band wagon and base our software architectures on these new, unproven technologies. This results in much time being spent on solving technology issues, rather than customer issues. And I should know, I have been (re)building frameworks over the past 20 years. Snail speed projects. Projects are often executed at snail speed, and have enormous amounts of slack-time wasted by people just waiting for other people to finish their work or waiting on decisions not being made. My worst case? I once spent 4 months lingering in a project that was on hold until a board high up in the organization - indeed, a large international bank - approved the new budget. And I wasn't the only one - there were 60 of us. The sun always shines As said, there is no technique or technology that will lift our productivity a 100-fold. No sir. Moreover, there is not even a combination of technologies that will do so, no matter how much vendors would like you to believe that they've just invented the silver bullet. We're just not there yet. But we could try. There are promising technologies, tools and methodologies. There always have been. Some ideas will help us get underway slowly and steadily (alas): Become agile. Letting go of large process and waterfall styled methodologies will produce better software at higher velocities. Agile software development is more cooperative, responsive to changes, and removes much of the air from waterfall styled projects. Not only in small, but also in very large and even distributed projects. After having evangelized agile software development for the past 10 years, I know see agile finally moving into the mainstream. Become communicative. Do not design and write your software under water, and submerge after months. Keep in touch with your customer during the whole project. Preferably execute (at least a large part) of your project at the customers' site. Let him or her be in the lead. Shorten your feedback cycles to a daily, if not hourly basis. Become standardized. Why does every new project define its own software architecture? And why are a large number of these new architectures so poorly designed? Why do we, developers, go along with every new (and often unfinished) technology fad? Standardization on good, sound architecture (domain driven of course) will help create better quality software that is easier to maintain and extend in the future. But be pragmatic, travel light. Even better, standardized software architectures allow for advanced, productivity increasing techniques, such as model driven software development and further down the road domain specific languages. Again, none of these ideas are the silver bullet to solve all our problems. And the current financial crisis doesn't help either. But, enough said. I'm not into pessimistic locking. We geeks have the best jobs in the world. The sun always shines from our asses. Just think of it. What other job will just let us write code all day? Sander Hoogendoorn blog.sanderhoogendoorn.org 46 CONFERENCE MAGAZINE EVENTS

47 INFORMATION WORKER UX Donald Hessing Silverlight biedt Nieuwe Mogelijkheden voor SharePoint Met de komst van Silverlight 2 is het ontwikkelen Rich Internet Applicaties (RIA s) een stuk eenvoudiger geworden. Naast de bekende animatie- en video-mogelijkheden biedt Silverlight 2 ook een aantal rijke controls die het gemakkelijker maken om SharePoint content op een dynamische manier te presenteren. Adobe timmert al jaren aan de weg met interactieve content op het web. De inmiddels ingeburgerde Flash-plugin is op bijna elke computer te vinden. Dat ook Adobe gelooft in Rich Internet Applicaties blijkt uit het in februari gelanceerde Flex 3. Daar waar Flash meer bedoeld is voor grafische doeleinden, is Flex meer bedoeld voor het ontwikkelen van RIA s. Ook Google timmert hard aan de Rich Internet weg met applicaties als GMail en Google Docs. Daar waar Adobe en Microsoft hun pijlen richten op een plugin voor de browser, ligt de focus bij Google op de open source-technologie Ajax. De technologie is voornamelijk gebaseerd op XML en Javascript, en juist om die reden heeft Google zijn nieuwe browser Chrome geoptimaliseerd voor Javascript. Het feit dat controls belangrijk zijn voor de adoptie van Silverlight bij ontwikkelaars is ook bij Microsoft geland Silverlight De eerste versie van Microsoft Silverlight is volledig gebaseerd op Javascript en is mede door het beperkte aantal controls geschikter voor het maken van leuke animaties dan voor het bouwen van RIA s. De nieuwe versie, Silverlight 2, is afgelopen oktober uitgebracht en bevat een rijkere controlset dan Silverlight 1. Daarnaast biedt het goede mogelijkheden om te communiceren met verschillende databronnen. Zo ondersteunt het o.a. het gebruik van web services en rss-feeds. Het feit dat controls belangrijk zijn voor de adoptie van Silverlight bij ontwikkelaars is ook bij Microsoft geland. Mede om die reden zal Microsoft tijdens de PDC (eind oktober) een nieuw team aankondigen of op het moment dat u dit leest, aangekondigd hebben - dat zich enkel bezig gaat houden met het ontwikkelen van controls voor Silverlight 2 en WPF. De verwachting is dat uiteindelijk 100 controls beschikbaar komen voor Silverlight 2. Eenvoudige presentatie van content De Silverlight 2-plugin is onderdeel van de browser waardoor de applicaties ook in de browser 'leven'. Voor het presenteren van data afkomstig van SharePoint zal het moeten communiceren met een service. SharePoint beschikt over een aantal services waarmee de content van het systeem opgehaald en bewerkt kan worden. Dat nog niet alles koek en ei is, blijkt uit de SharePoint-webservices die een datatype retourneren dat de Silverlight 2-runtime niet kent. Dit betekent dat de ontwikkelaar grofweg twee keuzes heeft: of hij ontwikkelt een eigen service-implementatie of hij ontleedt het XML-resultaat van de Sharepoint-webservice. Zodra de data ontvangen is, kan deze gepresenteerd worden aan de gebruiker. Hiervoor kan dus gebruik gemaakt worden van de controls die Silverlight 2 biedt. Juist het gebruik van de controls geven Microsoft en Adobe met hun plugins een voorsprong omdat Google op basis van Javascript en HTML werkt en hierdoor dit moeilijk kan invullen. Word previewer Voor documenten die opgeslagen zijn in SharePoint op basis van het nieuwe Office OpenXML-formaat (OOXML, tevens het Office 2007-formaat) biedt Silverlight 2 nieuwe mogelijkheden. Zoals de naam van de technologie doet vermoeden is OOXML volledig gebaseerd op XML. Hierdoor is het goed mogelijk een service te ontwikkelen die b.v. behalve de titel en de auteur van het document ook de eerste drie pagina's van het betreffende document ophaalt. De Silverlight-applicatie kan vervolgens de metadata van het document tonen, met daarbij een preview van de eerste paar bladzijden van het document. Het bedrijf Intergen uit Nieuw Zeeland lanceerde tijdens het MIX 2008-event in Las Vegas zelfs een volledige OOXML-reader voor Microsoft Worddocumenten. De oplossing is gebouwd in Silverlight 2 en geeft gebruikers de mogelijkheid om documenten te lezen vanuit de browser, zonder dat hiervoor Microsoft Word is vereist. RSS feed De makkelijkste manier om SharePoint content te tonen in een Silverlight-applicatie is door gebruik te maken van een RSS-feed. Op basis van de _layouts/listfeed.aspx pagina en de list guid als querystring parameter kan voor elke lijst de RSS-feed worden aangroepen en de data worden opgehaald. De Silverlight-applicatie kan de listfeed aanroepen door gebruik te maken van het HttpWebRequest-object dat een url verwacht, in dit geval de url van de SharePoint RSS-feed. Doordat communicatie in Silverlight altijd asynchroon plaatsvindt, moeten we een callback functie meegeven die wordt uitgevoerd zodra er antwoord is. De SyndicationFeed kan op basis van een XmlReader het resultaat van de RSS-feed inlezen. magazine voor software development 47

48 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES INFORMATION WORKER UX public void GetRSSFeed() // Roep de RSS feed aan string rssurl = " + "listfeed.aspx?list= %7B965400DD%2D34DF%2D49EA%2DA929%2D28FA4740AB66%7D"; HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create( new Uri(_RSSUrl)); request.begingetresponse(new AsyncCallback( RSSFeedResponseCallback), request); public void RSSFeedResponseCallback( IAsyncResult asynccallback) HttpWebRequest request = (HttpWebRequest)asyncCallback.AsyncState; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse( asynccallback); XmlReader reader = XmlReader.Create(response.GetResponseStream()); SyndicationFeed feed = SyndicationFeed.Load(reader); Dispatcher.BeginInvoke(delegate // Zet de text property van het textblock (xaml) MyTextBlock.Text = feed.items.tolist()[0].title.text; mystoryboard.begin(); ); Hoge kwaliteit streaming video De veelal ongestructureerde verzameling gegevens die we opslaan in SharePoint kan divers zijn. Een nieuwe trend is om naast teksten, documenten en foto's ook video- en audiobestanden vast te leggen. Video- en audiobestanden kunnen, afhankelijk van de kwaliteit en lengte, qua omvang oplopen tot enkele honderden Mb s. Dit maakt deze bestanden minder geschikt voor het opslaan in SharePoint. Daarnaast biedt het beperkte mogelijkheden om deze video- of audiobestanden op basis van streaming aan te bieden. Met behulp van Silverlight kan hoge kwaliteit video en audio online getoond worden. Hiervoor wordt de video of audio op basis van Silverlight streaming aangeboden. Voor het streamen van video kan er gebruik gemaakt worden van een aparte streaming server of van de gratis dienst Microsft Silverlight Streaming Service. De dienst biedt net als YouTube de mogelijkheid om je videobestand te uploaden naar de streaming server. Het plaatsen van het videobestand op een streaming server maakt de video nog niet bekend in SharePoint. Om dit te bereiken zal de metadata, zoals het onderwerp, titel, auteur en overige gegevens, over de video opgenomen moeten worden in SharePoint. Op basis van deze metadata kunnen specifieke kenmerken van een ecm-systeem, zoals zoeken, rechtenbeheer en workflow, worden gebruikt. Het opensource project Podcasting Kit for SharePoint (PKS) maakt gebruik van deze concepten, en biedt een oplossing voor hoge kwaliteit video toepassingen in SharePoint. De oplossing bestaat uit een sitedefinitie met daarin een aantal document libraries voor het opslaan van de metadata van de video s. Voor het uploaden van de video s maakt het gebruik van een Silverlight control. Listing 1: RSS-reader in Silverlight De volledige source code is beschikbaar op de website Complex ecm-systeem: de gebruiker merkt niets SharePoint is veelzijdig, maar daarbij ook complex. Voor gebruikers die er dagelijks mee werken zal dit geen probleem zijn. Zij zullen zich de eigenschappen en manier van werken eigen maken. Voor medewerkers die incidenteel gebruik maken van het systeem kan dit wel een drempel vormen. Een manier om de complexiteit van delen van het systeem weg te nemen is door hier een eigen user interface voor te maken. Deze schermt de complexiteit van het ecm-systeem af. Met behulp van Silverlight is het vrij eenvoudig functionaliteit als drag and drop, fotobewerking en het uploaden van meerdere documenten te realiseren. Het voordeel hiervan is dat de gebruiker geen kennis hoeft te hebben van de complexiteit die SharePoint of het user interface met zich meebrengt en dat er hele specifieke toepassingen gemaakt kunnen worden voor bepaalde doelgroepen. Het voordeel is dat er hele specifieke toepassingen gemaakt kunnen worden voor bepaalde doelgroepen Mobiele gebruikers kunnen een dergelijke doelgroep vormen. Share- Point biedt op dit moment beperkte ondersteuning voor mobiele apparaten. De verwachting is dat vanaf 2009 ook Silverlight 2 voor Windows Mobile en enkele Nokia-telefoons beschikbaar komt. Hierdoor wordt het mogelijk om specifieke functionaliteit van Share- Point te ontsluiten met een rijke gebruikersinterface voor de mobiele gebruikers. Fig. 1: Silverlight upload-control van de PKS De control voorziet in de mogelijkheid om meerdere video s parallel te uploaden en houdt daarbij de netwerkverbinding in de gaten. Na het uploaden wordt door een Windows service een preview gemaakt van de video en zorgt de service ervoor dat de geuploade video geschikt wordt gemaakt voor Silverlight streaming. Conclusie De mogelijkheden van Rich Internet Applications zijn talrijk en ze vormen de nieuwe tendens waarbij websites veranderen van platte HTML naar rijke user interfaces. Met de komst van Silverlight lijkt Microsoft wel een antwoord te hebben gevonden op Adobe, dat al langere tijd aanwezig is op het gebied van interactieve content voor het web. Mijns inziens zal Google met de open source-technologie Ajax vooral in het visuele aspect moeten inboeten ten opzichte van de plugins die Adobe en Microsoft bieden 48 CONFERENCE MAGAZINE EVENTS

49 Donald Hessing Donald Hessing is architect en technisch teamleider bij de unit Microsoft.NET System Development van VX Company IT Services BV. Hij heeft een brede interesse in techniek, en vervult o.a. de rol van lead architect binnen het Microsoft SharePoint team. Zijn focus ligt op het ontwikkelen van maatwerk oplossingen op basis van Share- Point en het Microsoft.Net Framework. Donald is de afgelopen jaren als technisch teamleider of architect verantwoordelijk geweest voor implementaties bij onder andere Interpolis, CZ en BCC. Naast zijn dagelijkse werkzaamheden geeft Donald regelmatig presentaties over praktische toepassingen van Microsoft technologie voor andere het Software Development Network en de Microsoft Developers Days. Delphi TIP: Delphi, dbexpress en de SQL zandloper Als je dbexpress gebruikt als data-access laag, dan zal het wellicht opgevallen zijn dat er alleen nog maar "gewone" zandlopers zichtbaar worden (tijdens het open van conneties of het uitvoeren van queries) en niet de SQL zandlopers (die we vroeger bij de BDE ook zagen). Dit is nog steeds zo in Delphi 2009 en 2007 (en was ook al bij Delphi 7 het geval). De oorzaak zit hem in de definitie van de constante HourGlassCursor in de unit SqlExpr.pas, die daarin de waarde -11 (Hour- Glass) krijgt in plaats van de waarde -17 (SQLWait). Advertentie Aladdin Wie liever de SQL zandloper terug wil, kan het volgende doen: 1. Kopieer SqlExpr.pas van de source\database directory naar je eigen project directory (zodat de compiler deze kopie zal gebruiken en niet de originele versie) 2. Open de unit SqlExpr.pas 3. Zoek de regel met de volgende code: HourGlassCursor = -11; (dat is regel 66 voor Delphi 2009, regel 62 voor Delphi 2007) 4. Maak er het volgende van: HourGlassCursor = -17; // was: Build je project opnieuw. Nu zul je weer de SQL zandloper zien bij het gebruik van dbexpress. Let op: mochten er later updates komen van Delphi die de oorspronkelijke SqlExpr.pas unit aanpassen, dan moet je bovenstaande stappen opnieuw uitvoeren (anders blijf je de SqlExpr.pas unit gebruiken zonder de eventuele nieuwe updates erin).

50 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES CORE SYSTEMS Michelle Cordes en Christiaan Heidema The Best of Both Worlds: Modernizing Core Sytems Core systems have been in place for the last 40 years. The last 10 years, however, the ICT environment has changed dramatically by developments such as Client Server, Internet and SOA. Central mainframe applications have been unlocked to client/server applications in the 90 s, via the internet around the millennium and now to SOA. Mainframe applications have been designed to fulfill one task only, while today s ICT demands more flexibility. How do Core System engineers cope with this challenge? With the aid of tools and 4 decades of engineering craftsmanship developers can quickly identify the business rules embedded in core business processes, restructure code, remove dead code and create reusable components that can be enabled as services within an SOA. This combination goes by the name of Enterprise Modernization. Software delivery processes and infrastructure supporting asset discovery and reuse Architectural approaches that leverage web technology enhancements Organizational skills to rapidly develop cross-platform applications. To modernize core systems, it is necessary to standardize and harmonize the software assets themselves as well as the software development process Fig. 1: Overview Enterprise Modernization Enterprise Modernization: the process Enterprise Modernization involves leveraging existing systems, improving the skills of IT staff and modernizing the architecture and infrastructure. It might be clear that transforming these changes all at once will be a very risky and finally a costly operation. Therefore, it s recommended to schedule the process of Enterprise Modernization in different steps. Determine the road to modernize Every company has to deal with particular business-requirements and is facing different staffing constraints. Furthermore, not all IT departments have the same budgetary limitations. Therefore, before starting the Enterprise Modernization process, it is necessary to determine the ultimate destination of the modernization process and clearly define the steps to reach the point on the horizon. This IT Modernization roadmap should acknowledge a holistic view of: Standardize and Harmonize To successful accomplish the modernization initiative, it is necessary to standardize and harmonize the software assets themselves as well as the software development process. Therefore, each modernization approach starts with assessing and reviewing existing systems to establish a common repository of the key application assets. Together with the knowledge received by these system reviews, this repository enables development teams to quickly discover and understand application relationships and dependencies, as well as assess the impact of proposed changes. The focus of development teams will be on a higher level, because their view is lifted up from one single application to more applications, including the underlying relationships. By using an integrated development environment for all platforms, development teams are able to break down the walls of their own development islands. All teams are working in according with the same standards and products and will anticipate and collaborate with other teams easily. Knowledge can also be shared across teams and will make the software development more flexible. Extending the integrated development environment with appropriate tools will lift up your software lifecycle-, change- and releasemanagement. Version control, deployment en defect tracking is done for all systems in the same way and affect the quality of your Software Lifecycle. Re-factor assets After the standardization and harmonization activities have been completed, you have established a solid foundation to proceed the modernization process. The next step of the modernization initiative is 50 CONFERENCE MAGAZINE EVENTS

51 CORE SYSTEMS to re-factor parts of the applications. During this step developers will identify the embedded business rules. When the business rules are determined, it is possible to restructure the assets and create reusable components. Dead code can be identified and removed. Reuse components The final step in the Enterprise Modernization approach is to invest in new opportunities. Because applications are transformed into more or less reusable components, extension of the functionality of applications will be much easier and cheaper than before. Consequently more budget is available for investing in new technologies and innovations like: Composite application development (SOA) Extend your applications by a web-based user-interface Transform towards business language The approach of Enterprise Modernization ensures the opportunities to achieve this higher level of software development. Applications are more flexible, reusable and easier to maintain and therefore increase business responsiveness to market demands and business requirements. Enterprise Modernization: the software perspective Now let s look at enterprise modernization from a software or application perspective. Core systems are facing enterprise modernization issues such as maintenance costs, using existing assets, architectural complexity, teams working in silos, cross-platform support and skills. The goal of Enterprise Modernization is to provide the capabilities that an organization requires to cost-effectively and incrementally evolve enterprise systems toward modern architectures and technologies. Tooling can help you to adapt business processes quickly and flexibly by reusing existing applications and data. Developers can unite and use their skills in disparate programming languages and work within a single, integrated development environment. This environment helps to form the foundation for your enterprise modernization initiatives. In addition, asset modernization tools can help you: Manage and govern the design, development and consumption of software assets and services; Eliminate the need to research, catalog and assemble the information for each service request; Identify assets that could be affected by proposed changes; Reduce the cost of ongoing application maintenance; Shorten the learning curve for new developers; Improve the productivity of existing IT staff. Asset modernization tools analyze the business software and identify components that can be reused Asset modernization tools can help analyze the business software and identify components that can be reused. Component identification within their applications provides an organization the ability to modernize incrementally to minimize risk and costs. According to Jeffery Poulin and Brent Carlson, the industry-average cost to develop new software is approximately 75 per line. This means that every 1,000 lines of reused code yields a Development Cost Avoidance of 60,000! This certainly makes the case for reusing existing code in any modernization effort. Every 1,000 lines of reused code yields a Development Cost Avoidance of 60,000! Support the lifecycle of assets When you begin the process of creating reusable assets, it is also important to properly manage and govern your services. By establishing a comprehensive picture of your software assets, you can improve asset reuse. In turn this can help you to quickly deliver innovative IT solutions, control costs, reduce application backlogs and improve business flexibility and responsiveness. Asset management tooling can help improve productivity and software delivery through asset reuse. This enables you to create, modify, govern and locate any type of development asset, including SOA and systems development assets. Analyze existing enterprise applications Often, companies have hundreds of project teams simultaneously making changes to application code and data structures. Fig. 2: Overview Asset Modernization Asset modernization: extending the value of existing enterprise assets Successful modernization initiatives require deep insight into targeted applications. Asset discovery and transformation tools can help your development teams generate detailed reports and graphics that enable rich understanding of existing applications. With this knowledge, developers can quickly identify the business rules embedded in core business processes, restructure code, remove dead code and create reusable components that can be enabled as services within an SOA. Fig. 3: Rational Asset Analyzer magazine voor software development 51

52 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES CORE SYSTEMS An automated asset-analyzer application, such as IBM Rational Asset Analyzer, can help engineers to cut through the complexity of the applications and interdependencies across the core systems to increase productivity and improve product quality. Rational Asset Analyzer scans mainframe and distributed software assets, stores related application information in a repository, and makes the data available in graphical and textual format through a web browser. Transform existing software assets into reusable components A key part of Enterprise Modernization is to identify reusable components and business rules that can be extracted and converted into a web service. Identifying these rules can help accelerate strategic and tactical modernization initiatives by allowing development teams to quickly transform existing assets and discover reusable business logic for creating services. A key part of Enterprise Modernization is to identify reusable components and business rules that can be extracted and converted into a web service Architecture modernization: driving innovation with technology advancements A modernization initiative must also address the complex dimensions of architecture. Fragmented business processes, workflows, data and tightly coupled application architectures reduce your flexibility and agility. To transform your core systems into flexible applications and services while avoiding costly and high-risk rip-and-replace approaches, you can work with what you already have. Architecture modernization can help to reduce time to market, improve business alignment for growth, cut costs and limit business risk. Design and construction tools are designed to: Speed the efficiency of core system development, web development and integrated mixed-workload development; Break skills silos by simplifying and accelerating cross-platform development; Increase productivity and reduce training costs by extending host applications to modern user interfaces; Accelerate the adoption of SOA by rendering existing IT assets as service components, which encourages reuse and efficiency; Create enterprise data standards, verify compliance and generate compliant models. Easily extend your applications to the web with reduced cost and risk Making existing mainframe applications available through the web can help extend their value while increasing efficiency and promoting asset reuse. Web applications provide a standard and easy-to-use graphical user interface (GUI) for your core system. These applications include portlets, rich client applications and applications targeted for browsers on mobile devices. Skills modernization: using and modernizing existing and new skills Traditional IT professionals have decades of experience and domain knowledge. The question is: how do you use this experience to improve your current enterprise applications and take advantage of the new architectures and technologies that are available on these platforms? An example of a tool that supports high development productivity is the platform-neutral, business-oriented Enterprise Generation Language (EGL). Because it s platform independent, EGL enables developers to build cross-platform applications and automatically generate and deploy native Java and COBOL code that s optimized for the target platform. EGL hides the details of the target execution platform and associated middleware, enabling developers to focus on the business problem rather than on the underlying implementation technologies. Even developers with little or no experience with Java and web technologies can use EGL to create enterprise-class services and applications quickly and easily. EGL can help you: Use new technologies and innovation without retraining the existing staff; Assign new employees to any project, no matter what the target platform is; Speed the efficiency of core system development, web development and integrated composite application development. Using EGL, a major advancement in business languages Because larger enterprises can have numerous development platforms and skill sets, a platform-neutral development approach can help eliminate skills silos and create a unified pool of business-oriented developers who can be freely shifted across projects according to business demands. EGL is a programming language specifically designed to help business-oriented developers leverage the benefits of mainframe and Java platforms without having to learn all the details. Enabling your developers to focus on the business problem, EGL features high-level specifications that let developers quickly write fully functional applications and services. IBM Rational Business Developer software enables developers to write business logic in EGL source code and then generate Java or COBOL code along with the run-time artifacts required to deploy the application to the desired execution platform. import com.acme.ordersys.api.*; handler Client type JSFHandlerview=CustomerOrders.jsp // Service references ordersvsiorderservice@bindservice OrderService"; // Fields bound to JSF components in the JSP custid CustomerId inputrequired= yes, mininput=9 ; orders Order[]; // Function bound to some button on JSP Function getorders() try orders = ordersvs.getordersforcustomer(custid); onexception( ex ServiceInvocationException) end end // Other event handlers end Listing 1: Example of EGL Client Code acme.ordersys.api.*; Service OrderService implements IOrderService Function getordersforcustomers( Custid CustomerID) returns(order[]) orders Order[]; get orders using custid; return( orders ); end End Listing 2: Example of EGL Service Code 52 CONFERENCE MAGAZINE EVENTS

53 CORE SYSTEMS EGL is easy to learn, whether your background is procedural or object oriented programming. We encourage you to join the growing community of EGL users and discover how this revolutionary technology can help you deliver innovative business solutions in record time. Process and infrastructure modernization: improving team collaboration and responsiveness Organizations have traditionally managed mainframe development separately from other platform developments. However, this separation not only can hinder collaboration and productivity across the software life cycle, it can also lead to errors that result in application failure or downtime. Tools for process, quality, and change and release management help automate and enforce development processes and enhance collaboration and productivity across multiple operating platforms throughout the application life cycle. These tools help you: Enforce software governance policies and procedures across functionally diverse and geographically distributed teams; Ensure that business goals and requirements drive downstream design, development and testing; Lower costs by eliminating duplicate tools and processes; Realize improved end-to-end communication and traceability across the life cycle; Verify software builds and document the exact software versions that are deployed; Manage quality across the software-delivery life cycle; Strategically integrate application security throughout the softwaredevelopment life cycle; Validate the scalability and reliability of complex applications before deployment. Govern change and release processes Solutions for change and release management can help boost productivity, improve visibility into projects and processes, unite distributed teams, and provide audit trails and traceability across the software-development life cycle for fast delivery of high-quality software. Tools can unify workflows for software configuration management and software change management. They can also help manage the software-development life cycle by using built-in replication and synchronization capabilities to provide integrated version control, workflow management and defect tracking. Enterprise change management can help protect your software assets globally, and help ensure that changes are linked to approved requests and that those changes are driven by a valid business requirement. Development investment modernization: enabling business flexibility Modernizing how you invest your development expenses is the final key to enterprise modernization. Investment in modernization includes moving investments to key platforms, architectures, and applications that can return maximum ROI. Organizations that continue to rely on inefficient existing applications and non-relational databases are finding that their ongoing maintenance costs are skyrocketing. To avoid this scenario, you need to make the transition to open, modular and proven software-development platforms that span the entire softwaredelivery life cycle. Make the transition to open, modular and proven software-development platforms that span the entire softwaredelivery life cycle Conclusion By modernizing core systems your applications will become more understandable, less complex and easy to extend. Business rules and complex interfaces between applications can be unlocked and transformed into solid and clear pieces of code. Spaghetti is transformed into smooth lasagna. Michelle Cordes Michelle Cordes has been working with IBM for more than 20 years. In addition to technical sales and marketing roles, she has helped customers in many industries to solve their business problems using IBM System z developer solutions. Michelle can be reached at mcordes@us.ibm.com. Christiaan Heidema Christiaan Heidema is Mainframe Software Guru at Sogeti Nederland BV. During the last 12+ year Christiaan has worked in several mainframe environments as DBA, Engineer and Change- and Configuration-manager. He developed a passion for mainframe software solutions. His expertise encloses inter alia COBOL, DB2, IMS, CICS and JCL..Net TIP: CSLA.NET for Silverlight Business applicaties maken met Silverlight 2? Rocky Lhotka heeft nu ook een versie van zijn populaire CSLA.NET framework voor het gebruik met Silverlight 2. Zie WindowsAndSilverlight.aspx voor meer details. magazine voor software development 53

54 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES.NET C# Sander van de Velde Reflection als Inpakpapier Probleemstelling Tijdens een recentelijk pilot-project is een nieuwe serviceinterface ontworpen om informatie uit te wisselen tussen twee bedrijven. Een webservice buiten de firewall (in de demilitarized zone, DMZ) moest als cliënt een applicatieserver binnen de firewall aanroepen. De oplossing was in eerste instantie in Windows Communication Foundation (WCF) ontwikkeld. Hierin is een interfacedefinitie namelijk snel geschreven en dan moeten alleen nog de WCF attributen ServiceContract en OperationContract op de serviceinterface en de bijbehorende methodes geplaatst worden (kijk voor meer informatie over WCF op Helaas bleek in een later stadium, dat op de webserver maximaal het.net 2.0 framework beschikbaar was en dus moesten we voor de communicatie uitwijken naar.net Remoting. Met.Net Remoting was eenzelfde oplossing met dezelfde methodes op een andere interfacedefinitie te bouwen. Maar één duidelijk verschil met WCF viel direct op: bij WCF maken de cliënt-proxy en de server van dezelfde service-interface gebruik, waardoor het in de WCF attributen nodig is om een referentie te leggen naar System.Service- Model op de cliënt, de aanroepende partij. Nu lijkt dit op zich triviaal, deze assembly is namelijk gewoon geïnstalleerd met het.net framework en dus altijd overal aanwezig. Maar opvallender is dat de gebruikte manier van communiceren zichtbaar wordt voor de aanroepende cliënt en dat het zelfs afdwingt, dat bepaalde gerefereerde assemblies geladen moeten worden. De WCF interfacedefinitie is dus een beetje opdringerig! De WCF interfacedefinitie is dus een beetje opdringerig Dit zou opgelost kunnen worden door gebruik te maken van een factory design pattern voor de communicatie. Design patterns zijn standaard oplossingen voor standaard problemen (zoals hier de abstractie van de communicatie) en binnen Atos Origin proberen wij altijd eerst terug te grijpen naar deze bewezen oplossingen. (Meer weten over design patterns? Fig. 1: Class diagram van factory pattern: de twee interfacedefinities zijn helaas niet gelijk Bij een factory design pattern wordt een instantie van het interface opgevraagd om bepaalde werkzaamheden uit te voeren, zonder kennis te nemen van de gemaakte keuze. De beslissing welke klasse wordt geïnstantieerd zal door de factory gemaakt worden. De factory kan bijvoorbeeld afhankelijk van het aanwezige.net platform de keuze tussen.net Remoting of WCF maken (zie figuur 1). Maar omdat de twee genoemde technieken ieder een eigen interfacedefinitie vereisen, met of zonder WCF attributen, moet hier een tweede design pattern toegepast worden. Wrapper design pattern In eerste instantie lijkt het niet mogelijk om WCF zonder de benodigde WCF attributen te laten communiceren. De WCF Proxy moet de benodigde kennis over de interface bezitten. Daarom is onderzocht of het mogelijk is om de cliënt WCF-proxy wel te blijven aanroepen, maar deze proxy in te pakken met een andere interface. De WCF-proxy aanroep moet tenslotte ergens uitgevoerd worden. Omdat hiermee alle communicatietechnieken met een uniforme, abstracte interface zijn aan te roepen, kan de factory de meest optimale techniek toepassen zonder dat de aanroepende cliënt zelf kennis over de communicatie krijgt. De oplossing voor dit probleem kan uitgewerkt worden in het wrapper design pattern (ook wel adapter genoemd). Dit wrapper design pattern wordt vaak toegepast, het is een fraaie manier om klassen samen te laten werken, die anders niet goed op elkaar aansluiten. De nieuwe interface wordt letterlijk als inpakpapier rond de aan te roepen logica gelegd (zie figuur 2). 54 CONFERENCE MAGAZINE EVENTS

55 .NET C# Maar inpakken zoals in het voorbeeld wordt al snel monnikenwerk vanwege het uittypen, vooral als het interface tijdens de ontwikkeling aan veel wijzigingen onderhevig is. Uiteindelijk bleek het gebruik van reflection samen met dynamische code generatie uitkomst te bieden om deze wrappers runtime te genereren zonder steeds extra te moeten coderen. Fig. 2: Class diagram van wrapper design pattern De wrapper ondersteunt geheel of gedeeltelijk de logica van de ingepakte instantie, maar laat zich aanroepen met zijn eigen interface. De IWrapperInterface heeft hierbij geen enkele relatie met IWrapped- Interface. IWrapperInterface hoeft dus niet eens alle members van IWrappedInterface te ondersteunen (zie listing 1). Het is nu ook mogelijk om bepaalde complexiteit van het ingepakte object bij het aanroepen te vereenvoudigen. public class WrapperClass : IWrapperInterface private IWrappedInterface _WrappedClass; public IWrappedInterface WrappedClass get return _WrappedClass; public WrapperClass(IWrappedInterface wrappedclass) _WrappedClass = wrappedclass; public string MethodOne(string parameter) return _WrappedClass.MethodOne(parameter); public int MethodTwo(int parameter) return _WrappedClass.MethodTwo(parameter); Reflection en MSIL generatie Met reflection wordt het mogelijk nieuwe types, compleet met methodes en logica, runtime in het geheugen te brengen als Microsofts Intermediate Language (MSIL). Dit is de ultieme just-in-time (JIT) codegeneratie, maar in de praktijk zie je het maar weinig toegepast worden. Enerzijds omdat de veilige wereld van de code-editor en de design-time compiler verlaten moet worden. Anderzijds is de leercurve hoog want het komt dicht bij het zelf op de stack zetten van attributen, het aanroepen van methodes om de stack uit te lezen en weer verder te vullen. (Kijk voor meer details op soft.com/en-us/library/8ffc3x75(vs.80).aspx) Wie ervaring heeft met assembler, beleeft hier het feest der herkenning. Toch is er een fundamenteel verschil met assembler. Ten eerste is er tegenwoordig IntelliSense en dat scheelt heel wat zoekwerk waar voorheen vaak een teksteditor het enige gereedschap was. Maar belangrijker is, dat deze MSIL wel degelijk typesafe is en blijft! Het is praktisch niet mogelijk om geheugenfouten te veroorzaken. De MSIL generator zal invalide opdrachten bij het samenstellen van het type gewoon afkeuren. Het gebruik van reflection betekent wel, dat ingeleverd moet worden op performance. Runtime MSIL generatie is echt enkele factoren trager dan het gebruik maken van voorgecompileerde code. Toch kan de runtime gegenereerde code opgeslagen worden, zodat later de assembly hergebruikt kan worden. Deze code zal dan geen snelheidsverlies veroorzaken. Dit opslaan van de assembly zal verderop gedemonstreerd worden. In System.Runtime.Remoting.Proxies wordt overigens de abstracte base class RealProxy aangeboden en deze geeft de mogelijkheid om twee interfaces op elkaar te mappen. Alle logica rond het inpakken wordt in één Invoke methode runtime uitgevoerd. Hierdoor is het geen vereiste dat de twee interfaces gelijkvormig zijn en is dus een andere mogelijke oplossing van de in dit artikel uitgewerkte oplossing. Wel zal de RealProxy altijd via reflection de mapping moeten uitvoeren en dus relatief trager blijven. Runtime wrapper generatie Onze wrapper gaat dus runtime twee willekeurige maar gelijkvormige interfaces op elkaar laten aansluiten. Hiervoor is een static helper class geschreven om het gewenste type te genereren (zie listing 2). Om tot een dergelijke dynamische wrapper te komen moeten vier stappen doorlopen worden, waarna het gewenste type klaar voor gebruik is. Dus voor iedere gewenste wrapper tussen twee interfaces moet deze helper apart aangeroepen worden. Listing 1: Een wrapper-implementatie met statische code Bij het voorbeeld in listing 1 zijn de twee interfaces wel gelijkvormig en daardoor is de wrapper zo dun mogelijk te houden. Toch kan met een wrapper nog meer gedaan worden dan alleen maar het doorlussen van de methodes. Zo kan bijvoorbeeld aan iedere aanroep logging, timing of extreme foutafhandeling toegevoegd worden. // Step 1: Generate the wrapper class type TypeBuilder typebuilder = GenerateWrapperType(typeOfWrapperInterface); // Remember the getter and setter MethodBuilder methodbuilderget; MethodBuilder methodbuilderset; Het gebruik van reflection samen met dynamische code generatie kan uitkomst bieden // Step 2: Generate the wrapped object property GenerateWrappedObjectProperty( typebuilder, typeofwrappedinterface, out methodbuilderget, out methodbuilderset); // Step3: Generate the constructor GenerateConstructor( magazine voor software development 55

56 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES.NET C# typebuilder, typeofwrappedinterface, methodbuilderset); // Step 4: Generate all methods GenerateWrappedMethodes( typebuilder, typeofwrappedinterface, methodbuilderget); // Finally, build this wrapper type and return return typebuilder.createtype(); Listing 2: De wrapper helper class aanroep met de vier stappen De eerste stap is van administratieve aard. Types kunnen niet gegenereerd worden zonder er een assembly voor te definiëren. Ook moet een module verplicht aanwezig zijn; dat is niet gelijk aan een namespace maar geeft wel de mogelijkheid om types te groeperen. Pas daarna is het mogelijk om met de bouw van het eigenlijke type te starten. Geef hierbij aan de typebuilder door dat een class aangemaakt moet worden en geef ook de overerving op (zie listing 3). Hier zal een overerving van de class Object de IWrapperInterface definitie gaan implementeren. // Define an assembly and a module AssemblyBuilder assemblybuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( new AssemblyName( typeofwrapperinterface.name + "ClassDll"), AssemblyBuilderAccess.Run); ModuleBuilder modulebuilder = assemblybuilder.definedynamicmodule( "Module" + typeofwrapperinterface.name); // Finally, define the type of the wrapper TypeBuilder typebuilder = modulebuilder.definetype( typeofwrapperinterface.name + "Class", TypeAttributes.Class TypeAttributes.Public, typeof(object), new Type[] typeofwrapperinterface ); return typebuilder; Listing 3: Beginnen met het definiëren van het wrapper type De tweede stap bestaat uit het mogelijk maken van het intern onthouden van het object dat de IWrappedInterface implementeert (zie listing 4). // The private field FieldBuilder fieldbuilder = typebuilder.definefield( "_WrappedObject", typeofwrappedinterface, FieldAttributes.Private); // The public property PropertyBuilder propertybuilder = typebuilder.defineproperty( "WrappedObject", PropertyAttributes.HasDefault, typeofwrappedinterface, null); // Define the public "get" accessor. methodbuilderget = typebuilder.definemethod("getwrappedobject", MethodAttributes.Public MethodAttributes.SpecialName MethodAttributes.HideBySig, typeofwrappedinterface, Type.EmptyTypes); ILGenerator ilgeneratorget = methodbuilderget.getilgenerator(); // Put 'this' on the stack ilgeneratorget.emit(opcodes.ldarg_0); // Load the field on the stack ilgeneratorget.emit(opcodes.ldfld, fieldbuilder); // Ready and return ilgeneratorget.emit(opcodes.ret); // Define the private "set" accessor. methodbuilderset = typebuilder.definemethod("setwrappedobject", MethodAttributes.Private MethodAttributes.SpecialName MethodAttributes.HideBySig, null, new Type[] typeofwrappedinterface ); ILGenerator ilgeneratorset = methodbuilderset.getilgenerator(); // Put 'this' on the stack ilgeneratorset.emit(opcodes.ldarg_0); // Put 'value' passed on the stack ilgeneratorset.emit(opcodes.ldarg_1); // Put the 'value' passed in the private field ilgeneratorset.emit(opcodes.stfld, fieldbuilder); // Ready and return ilgeneratorset.emit(opcodes.ret); // Now inform the property to use this setter // and getter methods propertybuilder.setgetmethod(methodbuilderget); propertybuilder.setsetmethod(methodbuilderset); Listing 4: De private field en de read-only property Eerst wordt een private field aangemaakt. Vervolgens wordt de publieke property gedefinieerd. Daarna worden de publieke getter en de private setter van de property gedefinieerd. Deze twee gedragen zich namelijk als methodes. In het algemeen voeren methodes code uit, dus die code moet ook gedefinieerd worden. Dit gebeurt met een ILGenerator en opcodes. Opcodes zijn de MSIL instructies, waarmee bijvoorbeeld doorgegeven parameters op de stack worden geplaatst en methodes worden uitgevoerd, die dan uiteraard van de stack lezen en er wellicht ook weer op terugschrijven (zie voor details over de hier gebruikte opcodes). Zoals wellicht bekend heeft een setter van een property een nogal magische value. Hier zie je dan ook hoe bij de setter methode gewoon al een waarde op de stack beschikbaar blijkt te zijn: Ldarg_1. De setter is dus inderdaad gewoon een methode. De getter en de setter worden teruggegeven aan de helper class, omdat we deze methodes later nog nodig hebben. De derde stap is het aanmaken van de constructor. De property wordt namelijk afgeschermd voor overschrijven, omdat de wrapper toch als IWrapperInterface wordt aangeroepen en die interface definitie kent geen property van het type IWrappedInterface (zie listing 5). De constructor ontvangt hierbij het ingepakte object als parameter en schrijft die simpelweg weg in het private field. Dit kan omdat een MethodBuilder een overerving is van een MethodInfo. 56 CONFERENCE MAGAZINE EVENTS

57 .NET C# // Define the constructor parameters Type[] constructorparameters = new Type[] wrappedinterfacetype ; // Load the field on the stack ilgenerator.emitcall( OpCodes.Call, methodbuilderget, null); // Define the constructor ConstructorBuilder constructorbuilder = builder.defineconstructor( MethodAttributes.Public MethodAttributes.RTSpecialName, CallingConventions.Standard, constructorparameters); ILGenerator ilgenerator = constructorbuilder.getilgenerator(); // Put 'this' on the stack ilgenerator.emit(opcodes.ldarg_0); // Put the wrapped object passed on the stack ilgenerator.emit(opcodes.ldarg_1); // Call the setter of the property ilgenerator.emitcall( OpCodes.Call, methodbuilderset, null); // Ready and return ilgenerator.emit(opcodes.ret); Listing 5: De constructor vult de property Als laatste stap moeten alle methodes van IWrappedInterface ondersteund gaan worden. Voor iedere te ondersteunen methode moeten alle door te geven parameters op de stack geplaatst gaan worden en daarna moet de methode van het ingepakte object aangeroepen worden om de logica uit te voeren (zie listing 6). Hiervoor maken we gebruik van de getter van de property. Een eventueel geretourneerde waarde uit de aanroep naar de methode van het ingepakte object wordt uiteindelijk gewoon weer terug op de stack gezet. // Put every passed parameter on the stack for (int j = 0; j < pia.length; j++) ilgenerator.emit(opcodes.ldarg, j + 1); // Call the method of the wrapped object ilgenerator.emit(opcodes.callvirt, methodinfointerfacetype); // Ready and return ilgenerator.emit(opcodes.ret); Listing 6: Alle methodes overnemen en doorlussen De MSIL generatie is afgerond. Kijk nu nog eens naar listing 2. Helemaal onderaan wordt vanuit de typebuilder het type gecreëerd. Mocht er zich nu ergens een probleem voor doen in de typebuilder (bijvoorbeeld een methode zonder voldoende of verkeerde parameters op de stack), dan zal hier een exception optreden. Als hier niet wordt geklaagd, dan hebben we nu eindelijk het gewenste type van de wrapper class. Om dit gegenereerde type te kunnen gebruiken zal dus de wrapper class geïnstantieerd worden, waarbij het ingepakte object aan de constructor meegegeven wordt (zie listing 7). Het gegenereerde type wordt dus via een activator in een runtime object omgezet, welke de IWrappedInterface ondersteunt. // Make an instance of the wrapped class IWrappedInterface wrappedclass = new WrappedClass(); MethodInfo[] methodinfoswrappedinterface = typeofwrappedinterface.getmethods(); foreach (MethodInfo methodinfointerfacetype in methodinfoswrappedinterface) //Put the parameter types in an array Type[] parametertypes = new Type[ methodinfointerfacetype.getparameters().length]; ParameterInfo[] pia = methodinfointerfacetype.getparameters(); for (int i = 0; i < pia.length; i++) parametertypes[i] = pia[i].parametertype; // Define the wrapper method for the wrapped method MethodBuilder methodbuilder = typebuilder.definemethod( methodinfointerfacetype.name, MethodAttributes.Public MethodAttributes.Virtual MethodAttributes.NewSlot, methodinfointerfacetype.returntype, parametertypes); ILGenerator ilgenerator = methodbuilder.getilgenerator(); // Put 'this' on the stack ilgenerator.emit(opcodes.ldarg_0); // Create the type of the wrapper Type wrappertype = WrapperHelper.CreateWrapperType( typeof(iwrapperinterface), typeof(iwrappedinterface)); // Make an instance of the type of the wrapper IWrapperInterface wrapper = (IWrapperInterface)Activator.CreateInstance( wrappertype, new object[] wrappedclass ); // Call the wrapper instance string returnvalue = wrapper. MethodOne("It is a wrap!"); Console.WriteLine(returnValue); Listing 7: Wrapper creëren en aanroepen Zelfreflectie voor de wrapper Met het gebruik van de wrapper is aangetoond, dat de wrapper-helper werkelijk iedere willekeurige interfacedefinitie met een andere interface definitie kan inpakken. Het is mogelijk om te controleren wat nu runtime gegenereerd wordt door de gegeneerde assembly op te slaan. Verander hiervoor de code in listing 3. De aanroep van DefineDynamicAssembly moet Assembly- BuilderAccess.RunAndSave meekrijgen zodat de assembly op schijf vastgelegd kan worden en de aanroep DefineDynamicModule moet een bestandsnaam meekrijgen ( bestandsnaam.dll ). Tevens moet de assemblybuilder als out parameter doorgegeven worden, want die wordt na het uiteindelijke samenstellen van het type (via Type Builder.CreateType) aangeroepen met het veelzeggende assembly- Builder.Save( bestandsnaam.dll ). magazine voor software development 57

58 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES.NET C# De runtime opgeslagen assembly is later gewoon te hergebruiken De runtime opgeslagen assembly is later gewoon te hergebruiken. Hierdoor is enigszins de grootste tekortkoming van het runtime genereren van code teniet gedaan, namelijk het relatief trage genereren. Open met bijvoorbeeld ILDasm de gegenereerde code en herken hier de code die zojuist met de ILGenerator samengesteld is (zie listing 8). Links: opcodes_members.aspx public newslot virtual instance string MethodOne(string A_1) cil managed // Code size 18 (0x12).maxstack 2 IL_0000: ldarg.0 IL_0001: call instance class [ReflectionDocumentClassLibrary] ReflectionDocumentClassLibrary. IWrappedInterface IWrapperInterfaceClass:: getwrappedobject() IL_0006: ldarg A_1 IL_000a: nop IL_000b: nop IL_000c: callvirt instance string [ReflectionDocumentClassLibrary] ReflectionDocumentClassLibrary. IWrappedInterface::MethodOne(string) IL_0011: ret Listing 8: MethodOne gezien door de ogen van IlDasm Het is zeker de moeite waard om dezelfde assembly ook eens met Reflector van Lutz Roeder te openen ( Met deze tool is het mogelijk om MSIL code naar andere.net talen (C#, VB.Net, Chrome, Delphi, etc) te reflecteren. Via de plug-in techniek van.net Reflector wordt deze lijst van talen regelmatig uitgebreid en zo is het inmiddels ook mogelijk om de MSIL code om te zetten naar broncode zoals in listing 2 t/m 6 (research.microsoft.com/~jhalleux/ - ReflectionEmitLanguage). Deze wrapper-helper is getest met het doorgeven van verschillende types. Het heeft geen moeite met value types, reference types, generic types, nullable types, etc. Ook kan voldoende omgegaan worden met exceptions vanuit de ingepakte klasse. Het feit dat de twee interface-definities ook echt op elkaar moeten aansluiten, wordt nog niet afgedwongen door deze te vergelijken. Ook wordt niet op een Null referentie getest voor het ingepakte object. Dit voorbeeld kan naar eigen inzicht aangepast worden en het uitbreiden met bijvoorbeeld logging van de aanroep met alle parameters erbij spreekt dan ook tot de verbeelding. Conclusie Voor mij was het bouwen van een dynamische wrapper een aangename kennismaking met MSIL. Ik heb gemerkt dat de leercurve eerst redelijk steil is, maar als je eenmaal bezig bent, valt alles redelijk te begrijpen. Een goede kennis van het.net framework is wel een vereiste. MSIL generatie is ontzettend krachtig. Het wordt mogelijk om zaken die normaal private gedefinieerd zijn uit te lezen en hierdoor zijn heel krachtige oplossingen te bouwen die de compiler normaal gesproken niet toelaat. Met de getoonde tools kan ook goed bekeken worden, hoe bepaalde code-constructies in MSIL gerepresenteerd worden. Dit is handig, want goede documentatie is schaars. Veel succes!.net TIP: Sander van de Velde Sander van de Velde is als senior.net ontwikkelaar werkzaam voor Atos Origin BAS Microsoft op de High Tech Campus te Eindhoven. Als na het programmeren nog tijd over blijft, gaat hij zich bezig houden met het coachen van zijn drie zoontjes, motorrijden of zeilen. Hij is te bereiken via sander.vandevelde@ atosorigin.com ASP.NET en JQuery JQuery is een kleine maar zeer populaire open source JavaScript library voor het gebuik in de browser. Microsoft staat niet helemaal bekend om zijn ondersteuning van open source maar in dit geval hebben ze besloten om JQuery mee te leveren in plaats zelf het wiel opnieuw uit te vinden. En ze leveren het niet alleen mee maar ze gaan het zelfs officieel ondersteunen. Zie weblogs.asp.net/scottgu/archive/2008/09/28/jquery-andmicrosoft.aspx voor meer informatie. Delphi TIP: Delphi 2009 onder Windows Vista Als je Delphi 2009 onder Windows Vista wilt installeren dan moet je de install.exe met de "Run as Administrator" optie draaien (anders kunnen niet de juiste directories en registry entries geschreven worden). Tevens komen de download files onder Windows Vista op de volgende plek: C:\Users\<username>\Documents\Rad_Studio_Downloads (een andere plek dus dan onder XP of 2003). Ook bij het starten van Delphi 2009 onder Windows Vista kun je het beste de "Run as Administrator" optie blijven gebruiken overigens. 58 CONFERENCE MAGAZINE EVENTS

59 Advertentie Sira Holding b.v.

60 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES.NET Piet Amersfoort Een Eerste Introductie in F# Microsoft heeft ervoor gekozen met.net verschillende typen programmeertalen te ondersteunen. Met de komst van F# wordt naast object georiënteerde, imperatieve, script- en dynamische talen ook een functionele programmeertaal aangeboden. Aan de hand van een aantal voorbeelden wordt in dit artikel getoond hoe F# werkt en hoe de bijbehorende programmeeromgeving gebruikt kan worden. Is keus goed? In de Computable verscheen na DevDays 2008 een artikel met de kop Microsoft-ontwikkelaars hebben te veel keus. #Computable#: Microsoft-ontwikkelaars kunnen kiezen uit zoveel verschillende platformen en talen dat ontwikkelaars door de bomen het bos niet meer zien Daar kan de volgende stelling van Bernard Baruch tegenover worden gezet: #Bernard Baruch#: If all you have is a hammer, everything looks like a nail Voor veel Microsoft-ontwikkelaars was er de keuze tussen C# en VB. Maar deze twee talen verschillen weinig van elkaar, ze zijn in veel opzichten een eeneiige tweeling. Het zijn beide object georiënteerde, imperatieve talen. Zo is het is bijvoorbeeld niet lastig een programma te schrijven dat VB-code vertaalt in C#-code en omgekeerd. In dit artikel zal F# aan de Microsoft-ontwikkelaar met.net-kennis (bijvoorbeeld C# of VB) worden voorgesteld. In Computable-termen is F# een nieuwe boom in het functionele programmeerbos. Het doel is de ontwikkelaar ertoe te bewegen om F# te downloaden en ermee te experimenteren. Dit artikel bevat geen theoretische verhandeling over functionele programmeertalen, maar aan de hand van enkele voorbeelden zal getoond worden hoe er met F# gewerkt kan worden. Er is niet gekozen voor de bekende ontwerpen uit.net, zoals object georiënteerd programmeren, hetgeen geen enkel probleem is met F#, maar voor voorbeelden waaruit de unieke eigenschappen van F# en de F#-programmeeromgeving blijken. De ontwikkelaar kan dan zelf bepalen welke programmeertaal het juiste middel is om een doel te bereiken. Het probleem is niet de keuzemogelijkheden van de ontwikkelaar, zoals Computable beweert. De ontwikkelaar moet ondersteund worden bij het maken van zijn keuzes, dat is de uitdaging. Wanneer heeft de ontwikkelaar een hamer nodig en wanneer een zaag? De F#-programmeeromgeving Somasegar (Senior Vice President Developer Division van Microsoft) heeft op 17 oktober 2007 aangegeven dat F# een volwaardige.nettaal wordt: # Somasegar: We will be partnering with Don Syme and others in Microsoft Research to fully integrate the F# language into Visual Studio and continue innovating and evolving F#. In my mind, F# is another first-class programming language on the CLR. Tot 29 augustus 2008 was F# te downloaden van de de Microsoftresearch website. Nu is de eerste CTP uit. Uit het blog van Somasegar (2 september 2008): # Somasegar: Last week, the F# team released the F# September 2008 CTP. This release marks an important step along the path we laid out in October to integrate the F# language into Visual Studio, and to continue innovating and evolving F#. F# is te gebruiken met.net van versie 2.0 tot en met 3.5 en MONO en wordt ondersteund door Visual Studio Nadat F# is geïnstalleerd, is het eerste dat opvalt dat F# op meer manieren gebruikt kan worden dan bijvoorbeeld VB of C#. Zo kunnen er scripts geschreven worden in F#. Listing 1 bevat een eenvoudig voorbeeld. #light printfn "Hallo SDN" System.Console.ReadLine() Listing 1: FsharpHalloSDN.fsx Als deze code in een bestand met de naam FsharpHalloSDN.fsx wordt opgeslagen, dan kan dit script direct vanuit de Windows-verkenner worden opgestart. Fig. 1: F# Interactive Figuur 1 laat zien hoe dit in de verkenner eruit ziet en figuur 2 toont het resultaat. Fig. 2: F# Interactive Hallo SDN Hiermee is het verplichte hello-world-programma geschreven. Een aantal zaken valt op: De instructie #light. F# kent twee varianten. Een variant waarbij het 60 CONFERENCE MAGAZINE EVENTS

61 .NET einde van een instructie expliciet wordt aangegeven (dat lijkt op VB) en een variant waarbij spaties (4 spaties) aangeven wat de structuur van een instructie is. De laatste variant heeft in de beschikbare literatuur en op het internet de voorkeur. Het vreemde is echter dat de keuze voor deze variant expliciet moet worden aangegeven met de instructie #light. Deze eis zal hoogstwaarschijnlijk in de toekomst verdwijnen. printfn is een instructie uit de F#-library en stuurt een string naar de console. Om te voorkomen dat na het opstarten van de console deze direct weer verdwijnt, is de voor.net-programmeurs vertrouwde System.Console.ReadLine-instructie toegevoegd. Hiermee is tevens aangetoond dat het.net-bouwwerk direct vanuit F# te benaderen is. Naast de scriptvariant is er ook een interactieve variant. In de F#-console kunnen direct instructies worden ingevoerd. In figuur 3 is een functie pluseen gedefinieerd die de waarde van een getal n met één verhoogt. Fig. 3: F# Interactive Na let volgt de naam van de functie (pluseen) en de naam van de parameter (n). pluseen werkt met integers, er gaat een integer in en er komt een integer uit (int -> int). F# leest geen gedachten maar leidt uit het plusteken en de waarde 1 af dat n ook een integer moet zijn en weet dat het resultaat ook een integer zal zijn. Als vervolgens de plus- Een van 3 wordt berekend. dan is het resultaat 4. Enkele andere zaken die opvallen: Het keyword let. Met let wordt aangegeven dat een waarde (val) wordt gedefinieerd. Een val is niet hetzelfde als een variabele, uit bovenstaand voorbeeld blijkt dat een functie ook een val is. De waarde van een val is vast. In C# en VB komt die overeen met const/readonly. De waarde van vier is 4 en blijft 4. ;; wordt in de interactieve omgeving gebuikt om het einde van een instructie aan te geven. Van F# Interactive zijn er twee varianten; de console-variant en de Visual Studio-variant. Zoals de naam al doet vermoeden werkt de laatste intensief samen met Visual Studio (VS). Hierdoor is het mogelijk om stukken code vanuit VS direct uit te voeren. Gebruikers van SQL Management Studio zullen dit herkennen. In figuur 4 wordt een stukje VS-code geselecteerd en met Alt+ Enter wordt deze uitgevoerd in VS-console. Fig. 4: F# Interactive in Visual Studio met Alt-Enter Een voordeel van deze wijze van werken is dat op een eenvoudige wijze code uitgevoerd en getest kan worden. Als laatste zijn er de voor de.net-ontwikkelaar bekende mogelijkheden om code om te zetten in.dll- en.exe-bestanden met behulp van console-instructies of Visual Studio. Deze bestanden zijn dan de gebruikelijke.net-bestanden en kunnen op elke computer waarop.net geïnstalleerd is, gebruikt worden. Tevens zijn deze bestanden te gebruiken als bibliotheek voor andere.net-ontwikkelaars in andere.net-talen. Een voorbeeld van functioneel programmeren: priemgetallen In het volgende voorbeeld zal getoond worden hoe in F# een probleem functioneel kan worden aangepakt. Hierbij worden de verschillen van aanpak met andere programmeertalen getoond. Als eerste volgt een VBA-voorbeeld (listing 2) van de functie IsPrime, deze functie kan in Excel gebruikt worden om te bepalen of een getal een priemgetal is. Public Function IsPrime(value As Integer) As Boolean Dim i As Integer i = 2 IsPrime = True Select Case value Case 1 IsPrime = False Case 2 IsPrime = True Case Else Do If value / i = Int(value / i) Then IsPrime = False End If i = i + 1 Loop While i < value And IsPrime = True End Select End Function Listing 2: VBA-functie die aangeeft of een getal een priemgetal is Met behulp van Google is de functie IsPrime gevonden op het internet en doet onder normale omstandigheden zijn werk. Voor wie het vergeten was: een priemgetal is een positief geheel getal dat alleen deelbaar is 1 en zichzelf. Nu zal getoond worden hoe in F# een functie gebouwd kan worden die hetzelfde resultaat oplevert (listing 3). #light let isdeelbaardoor n m = n % m = 0 let rec bevatfactor n van tot = if van >= tot then false else if isdeelbaardoor n van then true else bevatfactor n (van + 1) tot let ispriem n = match n with 1 -> false 2 -> true //staat er voor de duidelijkheid _ -> not (bevatfactor n 2 (n - 1)) Listing 3: F# versie van ispriem magazine voor software development 61

62 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES.NET In een functionele programmeertaal zijn de bouwstenen functies. In bovenstaand voorbeeld is de functie ispriem opgebouwd met behulp van twee hulpfuncties: isdeelbaardoor wordt gebruikt om te bepalen of een getal deelbaar is door een ander getal, isdeelbaardoor 10 5 = true, isdeelbaar- Door 10 6 = false. bevatfactor wordt gebruikt om de deelbaarheid van een getal n door een reeks getallen te bepalen. De reeks loopt van het eerste getal: van tot het laatste getal: tot. bevatfactor = true, bevatfactor = false. Elke functie uit listing 3 illustreert enkele interessante aspecten van F#. Wat er opvalt bij de functie isdeelbaardoor: % is het modulo teken in F# (hiermee wordt de rest van een deling berekend). Een getal is deelbaar door een ander getal als de rest van de deling nul is. In F# wordt niet de het dubbele isgelijkteken (==) gebruikt zoals in C#. Na het eerste gelijkteken volgen op de volgende regel vier spaties. Omdat in de #light-modus gewerkt wordt, wordt hiermee aangegeven dat deze regel onderdeel van de definitie van isdeelbaardoor is. Weer heeft F# geen type-informatie nodig om te bepalen dat n en m integers zijn en dat het resultaat een boolean is (val isdeelbaar- Door : int -> int -> bool). Er staan geen haakjes om de n en m. Het is mogelijk dit wel te doen (zie listing 4), maar daarmee verandert de functie van een functie met twee variabelen in een functie van één variabele (val isdeelbaardooralt : int * int -> bool). isdeelbaardooralt heeft als input een tuple van twee ints (int * int). Een tuple is een manier in F# een complex type te definiëren. In dit geval gaat de mogelijkheid verloren om met behulp van een functie van twee variabelen een nieuwe functie te definiëren met één variabele zoals in het voorbeeld uit listing 5. Op deze wijze een nieuwe functie definiëren wordt currying genoemd. Dit verklaart ook de schrijfwijze int -> int -> bool, als er een int wordt ingevuld dan ontstaat er een functie van int naar bool. #light let isdeelbaardooralt (n,m) = n % m = 0 Listing 4: alternatief voor isdeelbaardoor #light let isdeelbaardoortien n = isdeelbaardoor n 10 Listing 5: voorbeeld van currying Wat opvalt bij de functie bevatfactor: Na let volgt het keyword rec, dit is de afkorting van recursief. Een functie is recursief als deze zichzelf aanroept. Eén van de eisen aan een recursieve functie is dat er stopcriterium is. In dit geval is een criterium dat de reeks geen getallen bevat (van = tot) of het tot-getal kleiner is dan het van-getal. Als er een getal gevonden wordt dat een factor van n is, is een ander stopcriterium. Wat opvalt bij de functie ispriem: Naast de if then else constructie kent F# de mogelijkheid om patronen te matchen (pattern matching). In deze functie is een eenvoudig voorbeeld gebruikt. Pattern matching is te vergelijken met switch/select case in C#/VB maar biedt veel meer mogelijkheden. Van het patroon dat als eerste aan de criteria voldoet wordt de actie (de code na de pijl ->) uitgevoerd. Bij pattern matching is de underscore (_) de default, deze staat daarom onderaan in de lijst. Het resultaat in het geval n = 1 is daarom false. Met // kan er commentaar aan een regel worden toegevoegd. Blokken commentaar worden tussen ronde haken en astriksen geplaatst. Voorbeeld: (* commentaar *). In listing 6 is de functie bevatfactor uitgeschreven met behulp van pattern matching. #light let rec bevatfactoralt n van tot = match n, van, tot with _ when van >= tot -> false _ when isdeelbaardoor n van -> true _ -> bevatfactoralt n (van + 1) tot;; Listing 6: Pattern matching versie van bevatfactor Nu de F#-versie van de ispriem-functie is geanalyseerd, kan deze worden vergeleken met de VBA-versie. Als eerste valt op dat de F#-versie veel dichter bij de definitie van priemgetallen staat. Natuurlijk is het mogelijk de VBA-versie te herschrijven in deelfuncties en recursie te gebruiken (huiswerk), maar dit is niet de natuurlijke manier van programmeren in VBA. Ten tweede worden er geen hulpvariabelen gebruikt (de variabele i in de VBA-versie). Een reden dat deze variabelen niet worden gebruikt is dat waarden in F# standaard niet kunnen veranderen. Zoals eerder is aangegeven zijn waarden per definitie vaste waarden. Als er een variabele moet worden aangemaakt dan moet dit expliciet worden aangegeven met het keyword mutable. In listing 7 is als eerste de waarde j gedefinieerd. F# leest de volgende regel als een logische expressie en deze levert natuurlijk false op. #light //waarde van j is en blijft twaalf let j = 12 j = 13 //dit levert false op (* in dit geval kan de waarde van i worden gewijzigd *) let mutable i = 5;; printfn "de waarde van i is nu: %d" i;; //de waarde van i is nu: 5 i <- 7;; printfn "de waarde van i is nu: %d" i;; //de waarde van i is nu: 7 Listing 7: Waarden zijn niet te veranderen, mutable variabelen wel De waarde van i kan wel gewijzigd worden; hiervoor wordt het <- symbool gebruikt. Het gebruik van mutable variabelen wordt over het algemeen afgeraden binnen functionele programmeertalen. In listing 7 wordt weer de functie printfn gebruikt. Met %d kan worden aangegeven dat op deze plek een cijfer zal worden afgedrukt, de waarde van i. Als laatste opmerking: in de F#-versie van de ispriem-functie wordt geen gebruik gemaakt van een loop-instructie. F# kent dit type instructies wel, maar ook deze dient men spaarzaam te gebruiken binnen het functioneel programmeren. Recursie heeft de voorkeur. Voor het vervolg is er een functie nodig die het nde priemgetal kan bereken. Listing 8 bevat de code voor deze functie. Deze code bevat geen geheimen meer. volgendepriem is een hulpfunctie en berekent het eerst volgende priemgetal na een getal n (bijvoorbeeld volgende- Priem 11 = 13). NdePriem berekent met behulp van patten matching en recursie het nde priemgetal. 62 CONFERENCE MAGAZINE EVENTS

63 .NET #light let rec volgendepriem n = if ispriem (n + 1) then n + 1 else volgendepriem (n + 1) let rec NdePriem n = match n with 1 -> 2 _ -> volgendepriem (NdePriem (n - 1)) Listing 8: Berekening van het Nde priemgetal Lijsten met priemgetallen Er zijn vele aspecten van F# die nog beschreven kunnen worden, bijvoorbeeld types in F#, samenwerking met C#, VB en COM, lexers en yacc, etc. Er is gekozen voor lijsten. Hiermee kan namelijk ook een ander aantrekkelijk aspect van functionele programmeertalen worden behandeld, laziness. #light let lijst1 = [1..35] let lijst2 = List.map (fun n -> NdePriem n) lijst1 let lijst3 = [for n in > NdePriem n] Listing 9: Lijsten Een lijst (List) is in F# eenvoudig te definiëren. In listing 9 wordt als eerste een lijst gedefinieerd die alle getallen van 1 tot en met 35 bevat. Er kan een tweede lijst worden gedefinieerd door List.map los te laten op een functie die een integer in een integer omzet en een lijst. De functie die gebruikt wordt, is de functie die het nde priemgetal berekent (NdePriem) en de lijst zijn de getallen van 1 tot en met 35 (lijst1). Het resultaat is dus een lijst met de eerste 35 priemgetallen. Hetzelfde resultaat kan op de 3e manier bereikt worden; lijst3 is identiek aan lijst2. Het is dus mogelijk om in F# op heel eenvoudige wijze complexe lijsten te maken. F# kent daarom laziness: waarden worden pas berekend als deze werkelijk nodig zijn Een nadeel van deze manier van werken is dat op voorhand bekend moet zijn hoe groot de lijst gemaakt moet worden. Natuurlijk kan het risico op problemen worden verlaagd door een grote reeks te kiezen, maar dan kost het berekenen van deze getallen veel meer tijd en resources en er is een grote kans dat de informatie niet nodig is. F# kent daarom laziness (luiheid). Dit betekent dat waarden pas berekend zullen worden als deze werkelijk nodig zijn. In het voorbeeld in listing 10 wordt een lijst priemgetallen met alle priemgetallen gedefinieerd. De belangrijkste punten zijn: LazyList.unfold wordt gebruikt om de lijst te vullen. LizyList was onderdeel van F# en is met het uitbrengen van de eerste CTP verplaatst naar het PowerPack. Met #r kunnen referenties gemaakt worden naar.dll-en waaraan standaard niet gerefereerd wordt (pas het versienummer aan aan de versie van F# die geïnstalleerd is). De startwaarde is het eerste priemgetal 2. Met behulp van de functie volgendepriem wordt het volgende priemgetal berekend. Seq.take is een functie die van een LazyList een deellijst maakt van het type LazyList, in dit geval de eerste 35 priemgetallen. Seq.to_list is een functie die van het resultaat van Seq.take een lijst maakt. Waarden worden pas berekend als deze nodig zijn. De waarden worden bewaard. Als een waarde nogmaals gevraagd wordt, dan zal de waarde niet worden herberekend, maar direct beschikbaar zijn. Dit kan worden aangetoond door aan de functie volgendepriem een regel toe te voegen, zie listing 11. #light let rec volgendepriemalt n = if ispriem (n + 1) then printfn "de waarde is %d" (n + 1) //extra regel (neveneffecten) n + 1 else volgendepriemalt (n + 1) let priemgetallenalt = LazyList.unfold (fun n -> Some(n, volgendepriemalt n)) 2 let lijst5 = Seq.to_list (Seq.take 5 priemgetallenalt) lijst5 let lijst6 = Seq.to_list (Seq.take 6 priemgetallenalt) lijst6 Listing 11: Een aangepaste versie van de functie volgendepriem Het resultaat is zichtbaar in figuur 5. Als de waarden van lijst7 worden bepaald, dan wordt alleen het extra priemgetal berekend. #light Files\FSharp \bin\ FSharp.PowerPack.dll" let priemgetallen = LazyList.unfold (fun n -> Some(n, volgendepriem n)) 2 let lijst4 = Seq.to_list (Seq.take 35 priemgetallen) Listing 10: Laziness Fig. 5: Lazyness in actie Het afdrukken van de waarde van n wordt in functionele programmeertalen een neveneffect (side effect) genoemd. Het wordt afgeraden gebruik te maken van neveneffecten. Als deze echter noodzakelijk zijn, dan is het devies deze te scheiden van de overige code. Door neveneffecten kan de toestand (state) aangepast worden. Als functies geen neveneffecten bevatten, dan is de uitkomst van deze functies totaal deterministisch. De uitkomst van de functie is onafhankelijk van de plaats en de tijd waarop de waarde wordt berekend. magazine voor software development 63

64 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES.NET Er is nog één probleem met de lijst met priemgetallen. Ook al is deze lijst oneindig groot, het is een lijst van integers. Door Euclides is al bewezen dat er oneindig veel priemgetallen zijn en dit heeft tot gevolg dat priemgetallen oneindig groot worden en dat kunnen integers niet. Ook voor dit probleem heeft F# een oplossing. F# kent het type bigint. Het prettige van het rekenen met bigint is dat de waarde theoretisch onbegrensd is en in praktijk bepaald wordt door de fysieke eigenschappen van de computer. Om aan te geven dat een getal van het type bigint is wordt aan een cijfer een extra I toegevoegd (bijvoorbeeld 6I). #light let isdeelbaardoorbigint n m = n % m = 0I let rec bevatfactorbigint n van tot = if van >= tot then false else if isdeelbaardoorbigint n van then true else bevatfactorbigint n (van + 1I) tot let ispriembigint n = match n with 1I -> false 2I -> true //staat er voor duidelijkheid _ -> not (bevatfactorbigint n 2I (n - 1I)) let rec volgendepriembigint n = if ispriembigint (n + 1I) then n + 1I else volgendepriembigint (n + 1I) let priemgetallenbigint = LazyList.unfold (fun n -> Some(n, volgendepriembigint n)) 2I //test let priem = ispriembigint I let lijst7 = Seq.to_list (Seq.take 6 priemgetallenbigint) Conclusie Dit was een eerste introductie in F# met als doel dat de lezer F# heeft gedownload of zal downloaden. Vele aspecten van F# zijn niet behandeld en er is geen theoretische verhandeling over functionele programmeertalen gegeven. Er zijn zelfs geen criteria gegeven wanneer F# een betere keus is dan C# of VB. Natuurlijk is er een aantal van de omstandigheden duidelijk geworden wanneer F# voordelen kan hebben. F# kan beter omgaan met grote getallen en oneindige verzamelingen, maar deze aspecten van F# zouden overgenomen kunnen worden in C# of VB. Wat echter uiteindelijk niet overgenomen kan worden, is de denk- en werkwijze achter een functionele programmeertaal. Dat de mogelijkheid er nu is deze werkwijze te gebruiken als dat nodig is, is goed. Keuze is goed..net TIP: 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 is één van zijn specialisme. Sinds 2007 werkt hij met F#..NET Framework Client Profile De volledige versie van het.net framework is een flinke download. Voor veel applicaties is het hele framework echter niet nodig. Microsoft zag dit ook in en heeft nu een installatie beschikbaar die een subset, de.net Framework Client Profile, installeert in plaats van de volledige versie. Later upgraden naar het volledige framework blijft uiteraard mogelijk. Zie voor meer informatie. Listing 12: Een lijst met priemgetallen van het type bigint In listing 12 staan de aangepaste functies. Weer is F# in staat te bepalen wat de juiste types zijn. Het resultaat in figuur 6 toont dit aan. Alle functies werken nu met bigints. Tevens wordt van een groot getal bepaald of het een priemgetal is en de eerste zes priemgetallen worden berekend..net TIP: ClickOnce and Firefox Een van de nuttige nieuwtjes in het.net 3.5 service pack 1 is de Microsoft.NET Framework Assistant 1.0 die het mogelijk maakt om Click Once applicaties vanuit FireFox te starten. Zie NET35SP1.aspx voor meer informatie. Fig. 6: Alle functies zijn omgezet in bigint-functies Er zijn nog diversie optimalisaties mogelijk, maar dat zijn de huiswerkopdrachten voor de lezer. Zo is het bijvoorbeeld mogelijk bevatfactor te herdefiniëren met als variabelen een bigint en een lijst. Vervolgens is het mogelijk de waarden in deze lijst drastisch te beperken. Zo is het bijvoorbeeld voldoende dat de getallen in de lijst priemgetallen zijn. Dit klinkt als recursie 64 CONFERENCE MAGAZINE EVENTS

65 .NET ASP Michiel van Otegem ASP.NET onder de Motorkap: Controls Maken Ik heb een haat/liefde verhouding met ASP.NET custom controls. Aan de ene kant vind ik de elegantie van custom controls geweldig, maar aan de andere kant zit ik altijd weer te stoeien met de juiste oplossing voor ViewState, event- en postback-handling en de rendering van HTML. Zo ook weer met een recente control die ik wilde bouwen, een control die ik de AddRemoveListBox noem. Het idee achter de AddRemoveListBox is simpel. Voor ontwikkelaars die de control gebruiken moet deze control zich gedragen als een ListBox waarbij meerdere items tegelijk geselecteerd kunnen worden. Een voorbeeld van de gebruikersinterface zie je in figuur 1. worden. Aan m n tweede eis was nu echter nog niet voldaan. Aangezien de ListControl al alle items in de ViewState opslaat, is het overbodig dat de twee ListBox-controls voor de geselecteerde- en beschikbare items dat ook doen. Dit is simpel op te lossen door EnableViewState = false in te stellen op deze controls, maar dan werkt de PostBack afhandeling (en dus de hele control) niet meer. Een poging om dit te doen met de IPostBackDataHandler-interface mislukte. Deze interface gebruik je normaal gezien in een custom control om ervoor te zorgen dat data uit een PostBack afgehandeld wordt, maar door alles in child controls te regelen wordt de Load- PostData-methode op deze interface niet aangeroepen wat nu? Fig. 1: De AddRemoveListBox Een vergelijkbare control is wel beschikbaar van 3rd party vendors, maar met een groot ontwikkelteam gaat dat aardig in de papieren lopen. Ook gratis varianten die ik had bekeken deden net niet wat ik wilde. Perfectionistisch en eigenwijs als ik ben dacht ik zelf wel eventjes een variant te maken die wel aan mijn eisen voldeed. De belangrijkste eisen: 1) redelijk eenvoudig van opzet; 2) minimaal gebruik van ViewState. Het resultaat voldoet weliswaar aan m n eisen en is goedkoper (voor ons team althans) dan kopen, maar toch kostte het meer moeite en zit er eigenlijk een lelijke hack in. Om de control eenvoudig te houden ben ik uitgegaan van System.Web.UI.WebControls.ListControl. Dit is een abstracte class die alles al in zich heeft voor ListBox-achtige functionaliteit, zoals een collectie van ListItem objecten en ondersteuning voor DataBinding. In feite hoef je alleen de UI te verzorgen. Om de code simpel te houden gebruik ik child controls, zodat de HTML rendering beperkt blijft tot het groeperen van de controls. De opmaak van child controls kun je wijzigbaar maken door de ControlStyle-eigenschap van de controls toegankelijk te maken voor de buitenwereld, zoals in listing 1. public Style AddButtonStyle get return AddButton.ControlStyle; Listing 1: De stijl van een child control publiek maken Met twee Label-controls voor de kopjes, twee ListBox-controls voor de selectie en twee Button-controls voor de acties was ik klaar en had ik vrij snel een aardig werkende control die goed opgemaakt kon IPostBackDataHandler moet het afhandelen van Postback data elegant maken, doordat je een collectie met data krijgt die alleen van toepassing is op de control zelf. Als deze niet werkt, moet je echter anders te werk gaan. Dan is het handig dat ASP.NET gelaagd is opgebouwd is, waardoor je terug kunt grijpen op het good old Request-object, die al in Classic ASP een belangrijke rol vervulde. Met behulp van de UniqueID van de child control waarvoor je de teruggestuurde data wilt opvragen, haal je de waarde(s) uit het Request-object. Omdat er sprake is van multi-select, kan een ListBox-control meerdere waardes retourneren. In dat geval retourneert Request.Form[myControl.UniqueID] een komma-gescheiden string, hetgeen niet handig is als een waarde al een komma bevat. Minder bekend is dat je ook Request.Form.GetValues(myControl.UniqueID) kunt gebruiken om een array met strings op te vragen, zoals in listing 2 gedaan wordt. private void AddItems_Click(object sender, EventArgs e) String[] itemstoadd = Page.Requres.Form.GetValues(SelectList.UniqueID); if(itemstoadd!= null && itemstoadd.length > 0) foreach (String item in itemstoadd) Items.FindByValue(item).Selected = true; OnSelectedIndexChanged(new EventArgs()); Listing 2: Met het Request-object geselecteerd items uitvragen Listing 2 is een event handler voor de knop waarmee items toegevoegd kunnen worden aan de selectie. De waardes die geselecteerd zijn in de lijst met te selecteren items worden uitgelezen en aan de hand daarvan wordt het overeenkomstige item in de Items-collectie van de ListControl geselecteerd. De AddRemoveListBox-control kun je downloaden van mijn blog magazine voor software development 65

66 Advertentie Infodis Advertentie ianywhere Solutions/ a Sybase Company

67 ARCHITECTURE Steef-Jan Wiggers BizTalk Server: Architectuur Sinds de eerste lancering wint BizTalk Server aan populariteit en zijn we inmiddels met BizTalk Server 2006 R2 beland bij de vijfde versie van het product. De huidige versie R2 is een uitbreiding op de vorige waarin een aantal functionaliteiten is toegevoegd zoals de ondersteuning voor native EDI en RFID, WCF en SharePoint Services 3.0. Volgens de laatste cijfers van Microsoft werken wereldwijd meer dan 8000 klanten met BizTalk Server. Ook binnen ons land werkt een behoorlijk aantal bedrijven met BizTalk Server zoals SNS Reaal, Grontmij, De Friesland Zorgverzekeraar, UWV, Achmea, Interpolis en Stegeman. Dit is allemaal indrukwekkend, maar waar moet de klant nu rekening mee houden op moment dat het BizTalk Server in huis haalt? Waar bestaat het uit en hoe ziet een BizTalk Platform architectuur er nu uit? En tot slot, wat betekent het voor ontwikkelaars, information workers (business analisten) en architecten? In dit artikel zullen deze vragen beantwoord worden. Business context Wat zou de business (de klant) of IT bewegen tot de aanschaf van producten zoals BizTalk? Welke overwegingen of wensen leiden tot oplossingen waar BizTalk een rol in kan spelen? De business vandaag de dag moet flexibel zijn en snel op veranderingen in de markt kunnen inspelen. Het IT deel van een organisatie zal daarbij de eisen van de business volledig moeten begrijpen en kunnen inschatten. Daarnaast moet worden ingeschat, hoe deze business eisen impact hebben op de bestaande of nieuwe systemen. De meest voorkomende business eisen zijn collaboration, governance, ROI en de eerder genoemde flexibiliteit. Collaboration, ofwel samenwerking, binnen en buiten een organisatie, is belangrijk om de business te laten groeien. Dit geldt voor zowel het ad hoc versturen van berichten naar derde partijen, als voor de integratie van alle business functionaliteit binnen de organisatie. Governance, ofwel toezicht, heeft de business nodig om te kunnen voorzien in audit trails ten behoeve van wet- en regelgeving. En ten slotte wil de business altijd een return of investment (ROI) zien, zodat zij inzichtelijk hebben welke voordelen een systeem op termijn heeft. Waarom BizTalk? De hierboven genoemde eisen zijn allemaal legitieme business eisen, maar hoe past BizTalk hier nu in? BizTalk Server biedt de mogelijkheid om bijvoorbeeld legacy systemen te ontsluiten, systemen die voor de business onmisbaar zijn. Vaak zijn dit monolithische, starre systemen, die met behulp van BizTalk flexibeler en wendbaarder worden. Functionaliteit van dergelijke systemen wordt inzichtelijker en makkelijker onderhoudbaar. Dit is niet het enige argument om BizTalk in te zetten. Om het nog meer inzichtelijk te maken wat de argumenten kunnen zijn voor zowel business als de IT, volgt hieronder een overzicht van functionaliteiten Validatie van berichten. De structuur van een te ontvangen en/of te verzenden bericht wordt geanalyseerd en gevalideerd tegen een gedefinieerde structuur. De structuur wordt vastgelegd door middel van schema's. Transformatie van berichten. In de ideale wereld wordt er van één berichtdefinitie gebruik gemaakt. Meestal is dit echter niet zo en worden voor eenzelfde type object meerdere berichtdefinities gebruikt (schema's). De integratie-broker biedt standaard voorzieningen om berichten om te zetten naar een andere structuur (mapping van het ene schema naar het andere schema). Routering van berichten. Een ontvangen bericht wordt geanalyseerd en op basis daarvan doorgestuurd naar de geabonneerde applicaties/services. Routering kan plaatsvinden op basis van zogenaamde header-informatie of op berichtinhoud (content based routing). Aggregatie van berichten. Het samenvoegen van meerdere berichten tot een nieuw bericht (aggregator pattern). Naast dit pattern zijn er met de integratie-broker meerdere patterns te implementeren, zoals opspliten van berichten (splitter pattern), verzamelen van berichten en versturen naar meerdere ontvangers (gatter-scatter pattern). Betrouwbaarheid en beveiliging. Technisch-functionele diensten zoals gegarandeerde berichtaflevering, beveiligd transport, garantie van afzender en van oorspronkelijkheid van het bericht. Adapters en transport. Koppeling en communicatie naar applicaties en systemen. Orchestration (samenstelling). De mogelijkheid om bestaande diensten te combineren tot een nieuwe, gecombineerde dienst. Een voorbeeld hiervan is het aanbieden van locatie gebaseerde informatie: de service die de locatie bepaalt, wordt gekoppeld aan de service die informatie over de locatie oplevert. Door combinatie van twee 'eenvoudige' services ontstaat een nieuwe service/product. BAM (Business Activity Monitoring). Processen en/of samen - gestelde services kunnen door middel van voorafgestelde KPI's gemeten worden. BAM is de service die dit realiseert en biedt de mogelijkheid om via een cockpit de resultaten te analyseren en op magazine voor software development 67

68 FULL-COLOUR 5 0 c y a a n g e e l 5 0 z w a r t 1 5 c y a a n BEELDMERK DIVISIES ARCHITECTURE basis van deze analyse het proces aan te passen. Business to Business Integration. XML, EDI en dergelijke formaten worden ondersteund, zodat externe systemen kunnen communiceren met BizTalk. Ook kunnen Business processen in de ene organisatie communiceren met BizTalk processen in een andere organisatie. RFID Platform. Het platform binnen BizTalk voor ontwikkelen, uitrollen en management van Radio Frequency Identification (RFID) oplossingen, waarbij de meeste RFID apparaten worden ondersteund. Business Rule. Het toepassen van logica (regels) op processen (orchestrations) binnen BizTalk. Het raamwerk maakt het mogelijk declaratieve regels op te stellen, die een duidelijke betekenis hebben en gekoppeld kunnen worden aan feiten (zoals XML documenten, database tabellen of.net componenten). Management. Management van processen, berichten en applicatie artefacten via een centraal management console. Integratie met Visual Studio. Tools voor ontwikkelen, testen en uitrollen van BizTalk artefacten en oplossingen. Fig. 1: Schematische weergave van functionaliteiten binnen BizTalk Server 2006 R2 Nu we de business kant van het verhaal belicht hebben, gaan we het eens bekijken vanuit de IT kant. Daarbij kun je de vraag stellen hoe BizTalk past in hun landschap? Het antwoord is deels terug te vinden in de genoemde functionaliteiten van BizTalk. Adapters en transport zijn b.v. interessant, omdat daarmee koppelingen en communicatie naar andere applicaties en systemen mogelijk zijn. Binnen heterogene omgevingen kan BizTalk met haar adapters een belangrijke rol spelen om applicaties met elkaar te integreren Adapters Adapters die met BizTalk meegeleverd worden of beschikbaar zijn via Microsoft, stellen je in staat met diverse systemen te integreren zoals PeopleSoft, JD Edwards, SAP, Siebel, TIBCO, IBM Mainframes, IBM DB2 en Oracle. Daarbij worden allerlei communicatieprotocollen ondersteund zoals HTTP, FTP, SOAP, MQSeries, MSMQ. Data in de diverse systemen kunnen dus via allerlei communicatie protocollen door BizTalk ontsloten worden. Binnen heterogene omgevingen kan BizTalk met haar adapters een belangrijke rol spelen om applicaties met elkaar te integreren. Zeker wanneer binnen een organisatie Microsoft de voorkeur heeft boven leveranciers als IBM of Oracle, die vergelijkbare producten leveren zoals IBM WebSphere en Oracle Fusion. Orchestration Dit is zeker niet de enige interessante functionaliteit van BizTalk. Het kunnen ondersteunen van een zakelijk proces door meerdere diensten samen te stellen met behulp van Orchestration is een erg krachtige functionaliteit. Met behulp van het volgende denkbeeldige scenario wordt dit een stuk duidelijker. In het voorbeeld zullen ook de overige functionaliteiten van BizTalk de revue passeren. BizTalk zal zeer geschikt blijken te zijn voor geautomatiseerde machine naar machine processen. Een landelijk garage bedrijf stelt automobilisten in staat een afspraak te maken voor een servicebeurt via een website. De gebruiker kan, waar dan ook in Nederland, op een gewenst moment deze servicebeurt voor zijn auto laten plaatsvinden. Hij of zij zal via de website een verzoek doen voor een afspraak op een bepaald tijdstip bij een van de aangesloten garages. Een verzoekbericht van de afspraak (persoon, auto, tijdstip, locatie en dergelijke) zal worden verstuurd richting BizTalk Server, die het bericht inspecteert en valideert en vervolgens een proces start. Binnen het proces wordt het bericht verder ontleed en worden andere systemen geraadpleegd. We gaan even uit van een heterogene omgeving, waarbij een CRM applicatie (Siebel) aanwezig is en Oracle en DB2 databases. In dit scenario brengen de adapters van BizTalk uitkomst; immers, koppelingen met deze systemen worden ondersteund. Deze systemen krijgen het verzoekbericht toegestuurd, waar de informatie van het oorspronkelijke verzoekbericht in staat. Deze berichten worden binnen BizTalk aangemaakt, eventueel met behulp van een transformatie, en door routing worden de berichten naar de juiste systemen verstuurd. De systemen sturen hun antwoorden terug naar BizTalk. Deze antwoorden bepalen dan het verdere verloop van het proces. Uiteindelijk zal een succesvol verlopen proces leiden tot een afspraak. Deze afspraak zal dan aangemaakt worden in het systeem van de gewenste garage. Tot slot zal het proces eindigen in een bericht met tijdstip en locatie en deze informatie zal worden verzonden naar de website. De gebruiker zal dit als een bevestiging van zijn verzoek op de website zien. Het bovenstaande is een scenario als een proces succesvol verloopt. Dit is in de praktijk natuurlijk niet altijd het geval. De gebruiker zou dan een melding krijgen, dat de afspraak niet mogelijk is of dat er alternatieven zijn voor tijd en/of locatie. Omwille van de eenvoud hebben we dit buiten beschouwing gehouden. BizTalk Server Overzicht Duidelijk is wel welke functionaliteiten BizTalk Server bevat en wat het een organisatie te bieden heeft. Als architect, ontwerper en developer heb je ook behoefte de exacte werking van BizTalk te kennen en te weten uit welke componenten BizTalk bestaat en wat er onder de motorkap uitzit. BizTalk transformeert en persisteert alle berichten die het ontvangt, in een 'MessageBox' database op SQL Server om te kunnen voorzien in integratie van applicaties (applicatie-integratie) en coördinatie van logica tussen zakelijke processen (Business Process Management). Door middel van een adapter accepteert het berichten van externe applicaties, services, processen en systemen. BizTalk maakt daarnaast gebruik van een pijplijn aan de ontvangende kant om berichten te converteren naar XML Data. Daarna verwerkt BizTalk Server de berichten door middel van een Orchestration en routeert het verzoekbericht (request), of er wordt gebruikt gemaakt van een versturende pijplijn om XML data te converteren naar een ander formaat en door te sturen naar externe applicaties, diensten, processen en systemen. 68 CONFERENCE MAGAZINE EVENTS

69 ARCHITECTURE Bovenstaande beschrijft in hoofdlijnen de belangrijkste functies binnen BizTalk Alle data van en naar externe systemen via BizTalk zal de hierboven beschreven weg bewandelen (zie figuur 2). Dit is van wezenlijk belang bij het opzetten van een architectuur voor een BizTalk omgeving. Je kunt je namelijk richten op applicatie integratie en/of proces sturing binnen een organisatie. Belangrijk daarbij is het kunnen doorgronden van BizTalk Server Messaging Architectuur. componenten, die in volgorde worden uitgevoerd, die voorzien in een specifiek deel van de voorbereiding van een bericht voor publicatie zoals decryptie, parsing of validatie. Verzendpoorten Een verzendende poort is een combinatie van een versturende pijplijn (send pipeline) en een zend-adapter. Het is eigenlijk het omgekeerde van een ontvangende poort en haar ontvangst-locatie. Een verzendende poort kan ook nog worden gegroepeerd, zodat die de werking heeft zoals een distributielijst. De pijplijn wordt gebruikt om een bericht voor te bereiden en te versturen naar een andere service. De zend-adapter is verantwoordelijk voor het uiteindelijke versturen van het bericht en maakt daarbij gebruik van een protocol zoals SOAP of FTP. Orchestrations Orkestratie (orchestration) is een vormgegeven proces binnen BizTalk dat berichten ontvangt vanuit de 'MessageBox' welke voor hem bestemd zijn. Een orkestratie kan ook berichten publiceren door deze te versturen naar de 'MessageBox'. Een bericht verstuurd vanuit een orkestratie, wordt op een gelijke wijze in de 'MessageBox' geplaatst alsof het via een ontvangst-locatie wordt ontvangen. Fig. 2: Berichten stroom in/uitgaand BizTalk Server Messaging Architectuur De onderstaande figuur geeft een overzicht van een BizTalk architectuur vanuit een bericht (message) perspectief. Fig. 3: BizTalk messaging architectuur Een bericht wordt ontvangen door een ontvang (receive) locatie in een ontvangende poort. De volgende componenten zijn betrokken in het ontvangen, verwerken en versturen van berichten binnen BizTalk Server. Ontvangende poorten en ontvangst locaties Een ontvangende (receive) poort is een verzameling van een of meerdere ontvang-locaties, die de specifieke ingangen in BizTalk definiëren. Een ontvangst-locatie is de configuratie van een enkel eindpunt om berichten te kunnen ontvangen. De locatie bevat configuratie-informatie van zowel ontvangst-adapter als -pijplijn (pipeline). De adapter is verantwoordelijk voor het transport en het communicatiedeel van het ontvangen van een bericht. De pijplijn is verantwoordelijk voor voorbereiding van een bericht, voordat het in de MessageBox kan worden geplaatst (gepubliceerd). Een pijplijn bevat een reeks Berichten brievenbus (MessageBox) Database Het hart van de publish/subscribe engine in BizTalk is de Message- Box database. Deze bestaat uit twee componenten, nl. een of meerdere Microsoft SQL Server databases en een berichten-agent. De SQL Server database voorziet in persistentie van zaken als berichten, haar eigenschappen, de abonnementen hierop, de status van orkestraties, de volgdata (tracking) en de host wachtrijen (queues) voor routering. Host en host instanties Een host is een logische representatie van een Microsoft Windows proces, die BizTalk Server artefacten als zend-poorten en orkestraties uitvoert. Een host instantie (host instance) is een fysieke representatie van een host op een specifieke server. Een host kan een in-process host zijn, die wordt beheerd door BizTalk Server, of een geïsoleerde (isolated) host. In het laatste geval voert BizTalk Server de programmeercode in een niet door BizTalk gecontroleerd proces uit, b.v. via Internet Information Server (IIS) voor HTTP en SOAP. Hosts worden gedefinieerd voor een gehele BizTalk Server groep, wat een verzameling van BizTalk Servers kan zijn. Een BizTalk Server groep deelt de configuratie van MessageBox(en), poorten, etc. Deze componenten en hun gedrag/rol binnen BizTalk Server zijn van invloed op de wijze waarop de architectuur voor een BizTalk omgeving tot stand komt. Een belangrijke architectuurafweging richt zich op het wel of niet kunnen verwerken van berichten. Daarbij spelen volume en frequentie van berichten van elk interface dat de BizTalk omgeving gaat ondersteunen, een rol. Andere overwegingen spelen ook een rol bij een architectuur voor BizTalk. Waar worden b.v. de poorten (ontvang/verzend) onder - gebracht; samen op een machine met eventueel Orchestrations? Worden de berichten apart verwerkt of bewerkt? Gaan alle componenten draaien op enkele hosts op één enkele machine? Zo ja, dan betekent het dat deze voldoende resources (verwerkingskracht (CPU), geheugen en diskruimte) moet bezitten. Anders kunnen de componenten beter draaien in hosts verspreid over meerdere machines, zodat de belasting van de resources verspreid kan worden. Overwegingen die onder andere ook bij de uitrol van BizTalk Server een belangrijke rol spelen. Daarbij speelt ook de hoeveelheid aan omgevingen een rol zoals een ontwikkel-, test-, acceptatie- en productie-omgeving. Dit valt buiten de scope van dit artikel. BizTalk Server Platform Architectuur De functionaliteiten van BizTalk Server en haar werking zijn nu magazine voor software development 69

Aanmelden Na installatie wordt de service automatisch gestart en kunt u meteen aanmelden van op afstand:

Aanmelden Na installatie wordt de service automatisch gestart en kunt u meteen aanmelden van op afstand: Remote administratie Als administrator hebt u verschillende mogelijkheden om een Linux systeem van op afstand te beheren. Populaire toepassingen zijn bijvoorbeeld Webmin en VNC. Het gebruik van deze twee

Nadere informatie

Revisie geschiedenis. [XXTER & KNX via IP]

Revisie geschiedenis. [XXTER & KNX via IP] Revisie geschiedenis [XXTER & KNX via IP] Auteur: Freddy Van Geel Verbinding maken met xxter via internet met de KNX bus, voor programmeren of visualiseren en sturen. Gemakkelijk, maar niet zo eenvoudig!

Nadere informatie

Test Joomla op je PC 1

Test Joomla op je PC 1 1 Disclaimer Bij de samenstelling van dit ebook is de grootst mogelijke zorg besteed aan de juistheid en correctheid van de informatie die in dit ebook wordt verstrekt. De auteur van dit ebook kan op geen

Nadere informatie

WCF claims-based authorization

WCF claims-based authorization WCF claims-based authorization In elk project waar ik tot nu toe aan heb meegewerkt is beveiliging altijd een belangrijk onderdeel gebleken. De vraag is vaak wat de meest geschikte manier van authenticeren

Nadere informatie

VPN Remote Dial In User. Windows VPN Client

VPN Remote Dial In User. Windows VPN Client VPN Remote Dial In User Windows VPN Client VPN Remote Dial In User Met een Virtual Private Network (VPN) is het mogelijk om door middel van een beveiligde(geautoriseerd en/of versleuteld) verbinding te

Nadere informatie

Versturen van email vanuit een Delphi VCL toepassing

Versturen van email vanuit een Delphi VCL toepassing Versturen van email vanuit een Delphi VCL toepassing Voor Delphi bestaan uiteraard verschillende libraries om emails te versturen, maar in dit voorbeeld wordt een C# Dotnet library gebruikt en aangesproken

Nadere informatie

XAMPP Web Development omgeving opzetten onder Windows.

XAMPP Web Development omgeving opzetten onder Windows. XAMPP Web Development omgeving opzetten onder Windows. Inhoudsopgave 1. Lees dit eerst... 2 2. Inleiding... 2 1 Xampp downloaden... 2 2 Installatie Xampp 1.7.4 op externe harddisk... 3 3 XAMPP herconfiguren...

Nadere informatie

Praktijk opdrachten VMware

Praktijk opdrachten VMware Praktijk opdrachten VMware 1 1. Toegang tot de ICT Academie Cloud omgeving Om toegang te krijgen tot de Cloud omgeving van de ICT Academie, kun je onderstaande procedure volgen: http://wiki.vcloud.ictacademie.net/wp

Nadere informatie

Met een LightSwitch applicatie een OData service uit de Windows Azure Marketplace consumeren

Met een LightSwitch applicatie een OData service uit de Windows Azure Marketplace consumeren Met een LightSwitch applicatie een OData service uit de Windows Azure Marketplace consumeren Om eens wat ervaring op te doen met de Windows Azure Marketplace heb ik een publieke en gratis databron gekozen

Nadere informatie

VPN Remote Dial In User. DrayTek Smart VPN Client

VPN Remote Dial In User. DrayTek Smart VPN Client VPN Remote Dial In User DrayTek Smart VPN Client Inhoudsopgave VPN Remote Dial In... 3 Verbinding maken met de DrayTek router... 4 DrayTek VPN Remote Dial In configuratie PPTP VPN... 5 VPN verbinding opzetten

Nadere informatie

VPN Remote Dial In User. Windows VPN Client

VPN Remote Dial In User. Windows VPN Client VPN Remote Dial In User Windows VPN Client VPN Remote Dial In User Met een Virtual Private Network (VPN) is het mogelijk om door middel van een beveiligde(geautoriseerd en/of versleuteld) verbinding te

Nadere informatie

Planbord installatie instructies

Planbord installatie instructies Planbord installatie instructies Uit Comprise Wiki Inhoud 1 Basis installatie 1.1 Installeren 1.1.1 Microsoft Data Access Components 1.2 De eerste keer starten 2 Veelgestelde vragen 2.1 "Network resource

Nadere informatie

16. Web Station. In dit hoofdstuk komen de volgende onderwerpen aan bod:

16. Web Station. In dit hoofdstuk komen de volgende onderwerpen aan bod: 16. Web Station U kunt uw QNAP NAS gebruiken om een website te hosten. U kunt zelf een website bouwen in HTML of gebruik maken van één van de vele content management systemen die beschikbaar worden gesteld

Nadere informatie

Hier kunt u alle schijven en mappen afscannen op audio bestanden die ondersteund worden door de MP (mp3 en wma).

Hier kunt u alle schijven en mappen afscannen op audio bestanden die ondersteund worden door de MP (mp3 en wma). Netgear MP101 Dit apparaat speelt MP3's en WMV digitale bestanden en koppelt de stereo rechtstreeks aan de PC. Het apparaat werkt alleen in combinatie met een router of een wireless acces point. Er zit

Nadere informatie

Werken op afstand via internet

Werken op afstand via internet HOOFDSTUK 12 Werken op afstand via internet In dit hoofdstuk wordt uitgelegd wat er nodig is om op afstand met de ROS artikel database te kunnen werken. Alle benodigde programma s kunnen worden gedownload

Nadere informatie

Xampp Web Development omgeving opzetten onder Windows.

Xampp Web Development omgeving opzetten onder Windows. Xampp Web Development omgeving opzetten onder Windows. Inhoudsopgave 1. Lees dit eerst... 2 2. Inleiding... 2 3. Installatie Xampp... 3 1.1 Installatie Xampp Launcher... 7 1.2 Controle geïnstalleerde bestanden...

Nadere informatie

Sparse columns in SQL server 2008

Sparse columns in SQL server 2008 Sparse columns in SQL server 2008 Object persistentie eenvoudig gemaakt Bert Dingemans, e-mail : info@dla-os.nl www : http:// 1 Content SPARSE COLUMNS IN SQL SERVER 2008... 1 OBJECT PERSISTENTIE EENVOUDIG

Nadere informatie

VPN Remote Dial In User. DrayTek Smart VPN Client

VPN Remote Dial In User. DrayTek Smart VPN Client VPN Remote Dial In User DrayTek Smart VPN Client VPN Remote Dial In Met een Virtual Private Network (VPN) is het mogelijk om door middel van een beveiligde (geautoriseerd en/of versleuteld) verbinding

Nadere informatie

API...1 Identificatie...1 Opties...2 Acties...3 Webserver...6 Heartbeat...6 Buffer groottes...8

API...1 Identificatie...1 Opties...2 Acties...3 Webserver...6 Heartbeat...6 Buffer groottes...8 API API...1 Identificatie...1 Opties...2 Acties...3 Webserver...6 Heartbeat...6 Buffer groottes...8 Identificatie Alle programma's communiceren met elkaar door gebruik te maken van JSON objecten. Het normale

Nadere informatie

Javascript oefenblad 1

Javascript oefenblad 1 Leer de basis van Javascript. Javascript oefenblad 1 Niels van Velzen Javascript oefenblad 1 Pagina 2 Inleiding Javascript is niet altijd even makkelijk. Vooral aan het begin is het even wennen hoe de

Nadere informatie

Temperatuur logger synchronisatie

Temperatuur logger synchronisatie Temperatuur logger synchronisatie Juni 10, 2010 1 / 7 Temperatuur logger synchronisatie Introductie Twee of meerdere ontvangers van het Multilogger systeem kunnen met de temperature logger synchronisatie

Nadere informatie

Midi PDF Bladmuziek lezer

Midi PDF Bladmuziek lezer Inleiding. Ruim 20 ordners aan bladmuziek, meeste daarvan uitgeprint van een PDF. Even snel een nummer opzoeken wil dan ook niet, terwijl ik alles wel op alfabetische volgorde heb. Dat was het niet helemaal

Nadere informatie

Uitwerking Aanvullend tentamen Imperatief programmeren Woensdag 24 december 2014, 13.30 15.30 uur

Uitwerking Aanvullend tentamen Imperatief programmeren Woensdag 24 december 2014, 13.30 15.30 uur Uitwerking Aanvullend tentamen Imperatief programmeren Woensdag 24 december 2014, 13.30 15.30 uur 1. deze opgave telt voor 30% van het totaal. Schrijf een compleet programma, dat door de gebruiker vanaf

Nadere informatie

Rapport. i-bridge FleetBroker en LocationBroker. Versie 1.0. Datum 22 December 2010

Rapport. i-bridge FleetBroker en LocationBroker. Versie 1.0. Datum 22 December 2010 Rapport i-bridge FleetBroker en LocationBroker Versie 1.0 Datum 22 December 2010 Status Final Colofon IVENT A&A CDC Madame Curielaan 4-6 Postbus 20703 2289 CA Rijswijk Contactpersoon Patrick Brooijmans

Nadere informatie

Een gelinkte lijst in C#

Een gelinkte lijst in C# Een gelinkte lijst in C# In deze tutorial ga demonstreren hoe je een gelinkte lijst kan opstellen in C#. We gaan een klasse schrijven, die een gelijkaardige functionaliteit heeft als een ArrayList, namelijk

Nadere informatie

Organiseer uw verschillende SOAP services in één scenario

Organiseer uw verschillende SOAP services in één scenario 1 Organiseer uw verschillende SOAP services in één scenario Wouter Luijten wouterluijten@creetion.com 2 Introductie Tijdens de implementatie van een proces heeft u vaak te maken met een veelvoud aan services.

Nadere informatie

4/1.4 Service Location Protocol

4/1.4 Service Location Protocol Networking Services 4/1.4 Service Location Protocol 4/1.4.1 Inleiding Binnen Open Enterprise Server is het Service Location Protocol ( SLP ) een essentieel onderdeel. Dit protocol zorgt ervoor dat de clients

Nadere informatie

Eigen Widgets in CRM. Introductie. Limitering. Widgets beschikbaar stellen. Tips & Tricks Eigen Widgets in CRM

Eigen Widgets in CRM. Introductie. Limitering. Widgets beschikbaar stellen. Tips & Tricks Eigen Widgets in CRM Tips & Tricks Eigen Widgets in CRM Eigen Widgets in CRM Introductie De WebUI van CRM 7.0 maakt het mogelijk om je eigen widgets te maken en deze in je eigen view te gebruiken. Dat kan door gebruik te maken

Nadere informatie

Windows XP & Windows Vista

Windows XP & Windows Vista Rem ote Dial- in User Windows XP & Windows Vista Inhoudsopgave Inhoudsopgave... 2 Inleiding... 3 Verbinding maken met de router... 4 Remote Dial In User PPTP... 5 Nieuwe VPN-verbinding maken in Windows

Nadere informatie

Cloud2 Online Backup - CrashplanPRO

Cloud2 Online Backup - CrashplanPRO Cloud2 Online Backup - CrashplanPRO Handleiding- CrashplanPRO - Online Backup Download de clients hier: Windows 32- bit: http://content.cloud2.nl/downloads/back01- cra.backupnoc.nl/crashplan_x86.exe Windows

Nadere informatie

Inhoudsopgave Disclaimer... 3 Voorwoord... 4 Inleiding... 5 Het downloaden van XAMPP... 7 Het installeren van XAMPP... 8 Joomla installeren op

Inhoudsopgave Disclaimer... 3 Voorwoord... 4 Inleiding... 5 Het downloaden van XAMPP... 7 Het installeren van XAMPP... 8 Joomla installeren op 1 Inhoudsopgave Disclaimer... 3 Voorwoord... 4 Inleiding... 5 Het downloaden van XAMPP... 7 Het installeren van XAMPP.... 8 Joomla installeren op XAMPP... 15 Handige links... 16 2 Disclaimer Bij de samenstelling

Nadere informatie

NAT (Network Address Translation)

NAT (Network Address Translation) Technical Note #019 Auteur: Olaf Suchorski Gemaakt op: 11 juli 2000 Bijgewerkt op: 11 juli 2000 NAT (Network Address Translation) In deze Technical Note worden de meest voorkomende situaties met NAT doorgelicht.

Nadere informatie

Handleiding Magento - Yuki

Handleiding Magento - Yuki Handleiding Magento - Yuki www.webwinkelfacturen.nl Samenvatting Dit is de handleiding voor de koppeling van Magento naar Yuki. De koppeling zorgt dat voor facturen in Magento automatisch een factuur of

Nadere informatie

Aan de slag met Twitter

Aan de slag met Twitter Aan de slag met Twitter Registreren De URL (het adres op het internet) om te registreren is: https://twitter.com/signup In dit voorbeeld is er een Twitter 1 account aangemaakt voor een woning die te koop

Nadere informatie

Technote. EnGenius Senao EOM Mesh Layer 2 configuratie Transparant netwerk

Technote. EnGenius Senao EOM Mesh Layer 2 configuratie Transparant netwerk Technote EnGenius / Senao EOM-8670 Mesh Layer 2 configuratie Transparant netwerk Merk Model Firmware Datum EnGenius Senao EOM-8670 2.1.10 09-04-2009 Pagina 1 van 29 Inhoudsopgave Inhoudsopgave...2 Node

Nadere informatie

XML Web Services of.net Remoting? W ANNEER GEBRUIK JE WELKE TECHNOLOGIE VOOR DE AANROEP VAN REMOTE SERVICES

XML Web Services of.net Remoting? W ANNEER GEBRUIK JE WELKE TECHNOLOGIE VOOR DE AANROEP VAN REMOTE SERVICES Anko Duizer is trainer en consultant bij Class-A te Woerden http://www.class-a-.nl XML Web Services of.net Remoting? W ANNEER GEBRUIK JE WELKE TECHNOLOGIE VOOR DE AANROEP VAN REMOTE SERVICES In dit artikel

Nadere informatie

b-logicx handleiding INHOUDSOPGAVE VPN verbinding voor Windows XP UG_VPN.pdf

b-logicx handleiding INHOUDSOPGAVE VPN verbinding voor Windows XP UG_VPN.pdf VPN verbinding voor Windows XP INHOUDSOPGAVE 1. Inleiding 2 2. Wat is de bedoeling? 3 2.1 Waarom een VPN verbinding 3 2.2 Wat is zeker niet de bedoeling? 3 2.3 Wat heb je nodig? 3 3. Instellen van de VPN

Nadere informatie

De SAP Cloud Connector 2.0 maakt SAPUI5 ontwikkeling via de WEB-IDE mogelijk met data uit je eigen backend systeem.

De SAP Cloud Connector 2.0 maakt SAPUI5 ontwikkeling via de WEB-IDE mogelijk met data uit je eigen backend systeem. De SAP Cloud Connector 2.0 maakt SAPUI5 ontwikkeling via de WEB-IDE mogelijk met data uit je eigen backend systeem. Vele van ons willen wel eens spelen met de WEB-IDE in de could via het SAP Trial Hana

Nadere informatie

VKblog-importer : De gebruiksaanwijzing.

VKblog-importer : De gebruiksaanwijzing. VKblog-importer : De gebruiksaanwijzing. deze gebruiksaanwijzing beschrijft versie 0.1.2 VKblog-importer is een plugin voor WordPress die bedoeld is om blogs van VKblog te importeren in WordPress. Posts

Nadere informatie

SSL VPN. In deze handleiding zullen wij onderstaande SSL mogelijkheden aan u uitleggen. - SSL VPN account/groep creëren.

SSL VPN. In deze handleiding zullen wij onderstaande SSL mogelijkheden aan u uitleggen. - SSL VPN account/groep creëren. SSL VPN SSL VPN SSL VPN is een web based versie van VPN waarbij er geen VPN client software nodig is. Het wordt niet beperkt door netwerkomgevingen en is zeer eenvoudig te configureren. SSL staat voor

Nadere informatie

Configureren van een VPN L2TP/IPSEC verbinding

Configureren van een VPN L2TP/IPSEC verbinding Configureren van een VPN L2TP/IPSEC verbinding Inhoudsopgave 1. Voorbereiding.... 3 2. Domain Controller Installeren... 4 3. VPN Configuren... 7 4. Port forwarding.... 10 5. Externe Clients verbinding

Nadere informatie

Installatie van Linux Mint 13

Installatie van Linux Mint 13 Installatie van Linux Mint 13 De installatie van Linux Mint 13 is zeer eenvoudig. Download de Mint 13 DVD ISO image van http://www.linuxmint.com/download.php en brand deze op een dvd en start uw computer

Nadere informatie

Managed Online Backup

Managed Online Backup Managed Online Backup Installatie instructies Backup Manager voor server Versie : 1.6 Auteur : Ingrid de Bont Datum : 18-10-2011 Inhoudsopgave 1 Installeren Backup Manager... 2 1.1 Aandachtspunt voor backuppen

Nadere informatie

NHibernate als ORM oplossing

NHibernate als ORM oplossing NHibernate als ORM oplossing Weg met de SQL Queries Wat is ORM? ORM staat in dit geval voor Object Relational Mapping, niet te verwarren met Object Role Modeling. ORM vertaalt een objectmodel naar een

Nadere informatie

Aquo Domeintabellen Services (Aquo DS) Handleiding Webservice

Aquo Domeintabellen Services (Aquo DS) Handleiding Webservice Aquo Domeintabellen Services (Aquo DS) Handleiding Webservice handleiding voor de programmeur Auteur: Informatiehuis Water Documentbeheer Wijzigingshistorie Datum Versie Auteur Wijziging Aat van den Heuvel

Nadere informatie

Handleiding Wordpress

Handleiding Wordpress Handleiding Wordpress Inhoudsopgave 1. Inloggen 2. Berichten en Pagina s 3. Afbeeldingen en video s 4. Weblinks 1. Inloggen 1.1 Inloggen bij Wordpress We starten met het inloggen op je WordPress gebaseerde

Nadere informatie

ArcGIS Mobile ADF. Smart Client Applicaties voor ArcGIS Server Eva Dienske, Wim Ligtendag

ArcGIS Mobile ADF. Smart Client Applicaties voor ArcGIS Server Eva Dienske, Wim Ligtendag ArcGIS Mobile ADF Smart Client Applicaties voor ArcGIS Server Eva Dienske, Wim Ligtendag Agenda Wat is de Mobile ADF? Architectuur Demo Wat is de mobile ADF? Ontwikkeltoolkit voor mobiele (Smart Client)

Nadere informatie

Downloaden van Software.

Downloaden van Software. Downloaden van Software. Op een Computer staan altijd een enorme hoeveel programma's en bestanden, soms erg klein, maar ook hele uitgebreide bestanden. Soms is er behoefte om een ander programma uit te

Nadere informatie

Er wordt door veel mensen opgezien tegen de overstap

Er wordt door veel mensen opgezien tegen de overstap With a little Help from Wennen aan Office 2010 John Spronk Er wordt door veel mensen opgezien tegen de overstap naar Office 2010 omdat het er zo anders uitziet dan het vertrouwde Office 97. Degenen die

Nadere informatie

Beveiligingsbeleid. Online platform Perflectie

Beveiligingsbeleid. Online platform Perflectie Beveiligingsbeleid Online platform Perflectie 2018 Beveiligingsbeleid Perflectie Versiebeheer Naam Functie Datum Versie Dimitri Tholen Software Architect 12 december 2014 1.0 Dimitri Tholen Software Architect

Nadere informatie

JPTrain. JPTrainBeta versie 25 mei 2015. Android client voor GBtrainHost

JPTrain. JPTrainBeta versie 25 mei 2015. Android client voor GBtrainHost JPTrain JPTrainBeta versie 25 mei 2015 Android client voor GBtrainHost Inhoud 1. Benodigd voor JPTrain... 3 2. Installatie JPTrain... 3 2.1 Conversie van oude versie(s)... 3 3. Eerste kennismaking met

Nadere informatie

10/5 Integratie met Windows

10/5 Integratie met Windows Integratie 10/5 Integratie met Windows 10/5.1 Novell Domain Services for Windows 10/5.1.1 Inleiding Tot de belangrijkste vernieuwingen in Open Enterprise Server 2 SP 1 dat in december 2008 is uitgekomen,

Nadere informatie

Siemens workpoints en DHCP options

Siemens workpoints en DHCP options Siemens workpoints en DHCP options Dit document beschrijft de configuratie en werking van een Windows 2003 DHCP server in combinatie met Siemens optipoint en Siemens OpenStage toestellen (aangemeld op

Nadere informatie

Dynamiek met VO-Script

Dynamiek met VO-Script Dynamiek met VO-Script Door Bert Dingemans DLA Ontwerp & Software bert@dla-architect.nl Inleiding Op de SDGN nieuwsgroep voor Visual Objects ontstond laatst een draad van berichten over de nieuwe libraries

Nadere informatie

HET BESTURINGSSYSTEEM

HET BESTURINGSSYSTEEM HET BESTURINGSSYSTEEM Een besturingssysteem (ook wel: bedrijfssysteem, in het Engels operating system of afgekort OS) is een programma (meestal een geheel van samenwerkende programma's) dat na het opstarten

Nadere informatie

Installatie Loon 2015

Installatie Loon 2015 Installatie Loon 2015 Inhoud Loon 2015 downloaden van MijnLoon.nl...1 Nooit eerder met Loon gewerkt?...1 Onbekende uitgever...2 Windows 8: Onbekende app...2 Installatieschermen...3 Windows Start, Alle

Nadere informatie

Technische FAQ koppelvlak WUS 2.0 voor bedrijven

Technische FAQ koppelvlak WUS 2.0 voor bedrijven Technische FAQ koppelvlak WUS 2.0 voor bedrijven Versie 1.0 Datum 25 juli 2012 Status Definitief Colofon Projectnaam Versienummer Contactpersoon Organisatie Logius Postbus 96810 2509 JE Den Haag servicecentrum@logius.nl

Nadere informatie

Kleine cursus PHP5. Auteur: Raymond Moesker

Kleine cursus PHP5. Auteur: Raymond Moesker Kleine cursus PHP5 Auteur: Raymond Moesker Kleine cursus PHP PHP is platform en CPU onafhankelijk, open source, snel, heeft een grote userbase, het is object georiënteerd, het wordt omarmd door grote bedrijven

Nadere informatie

Degrande Frederik COMPUTER OVERNEMEN januari 2005

Degrande Frederik COMPUTER OVERNEMEN januari 2005 Een computer via internet overnemen Via internet kun je de bediening van een computer overnemen. Heel handig, als je iemand met een probleem wil helpen of iets wil demonstreren. Soms is het zeer handig

Nadere informatie

Technical Note. API Beschrijving Aangetekend Mailen

Technical Note. API Beschrijving Aangetekend Mailen AUTHOR APPROVED Technical Note API Beschrijving Referentie: API beschrijving AM Versie: 0.0.7 Datum: 2015-07-24 Aangetekend Bellen B.V. Computerweg 5 Postbus 8307 3503 RH Utrecht T: +31 346 581 731 support@aangetekendmailen.nl

Nadere informatie

4 ASP.NET MVC. 4.1 Controllers

4 ASP.NET MVC. 4.1 Controllers 4 ASP.NET MVC ASP.NET is het.net raamwerk voor het bouwen van webapplicaties. De MVC variant hiervan is speciaal ontworpen voor het bouwen van dergelijke applicaties volgens het Model-View-Controller paradigma.

Nadere informatie

Installatie MicroSoft SQL server 2012 Express

Installatie MicroSoft SQL server 2012 Express Installatie MicroSoft SQL server 2012 Express Het installeren van deze MicroSoft SQL server 2012 Express dient te gebeuren door iemand met volledige rechten op het systeem. Wij adviseren dit door een systeembeheerder

Nadere informatie

ProjectHeatmap. Onderzoeksrapport v0.5 11-03-11 Dennis Wagenaar

ProjectHeatmap. Onderzoeksrapport v0.5 11-03-11 Dennis Wagenaar ProjectHeatmap Onderzoeksrapport v0.5 11-03-11 Dennis Wagenaar 1 Inhoudsopgave Inleiding...3 Gheat...4 Info...4 Voordelen...4 Nadelen...4 Google Fusion Tables...5 Info...5 Voordelen...5 Nadelen...5 OLHeatmap...6

Nadere informatie

Softphone Installatie Handleiding

Softphone Installatie Handleiding Softphone Installatie gids Softphone Installatie Handleiding Specifications subject to change without notice. This manual is based on Softphone version 02.041 and DaVo I en II software version 56.348 or

Nadere informatie

5/5 Red Carpet. 5/5.1 Inleiding

5/5 Red Carpet. 5/5.1 Inleiding Management Services 5/5 Red Carpet 5/5.1 Inleiding Met de overname van Ximian is Novell ook eigenaar geworden van de Red Carpet-technologie. Hoewel het aannemelijk is dat het hier een tijdelijke oplossing

Nadere informatie

Inhoudsopgave. Hoofdstuk 1.RMI...2

Inhoudsopgave. Hoofdstuk 1.RMI...2 - CORBA Inhoudsopgave Hoofdstuk 1.RMI...2 1.1.Inleiding...2 1.2.De remote...4 1.3.Het remote...5 1.4.De server...6 1.5.De server opstarten...8 1.6.De client applicatie...8 1.7.De stub en skeleton en...10

Nadere informatie

Priva Blue ID Network scanner / Syslog Tool

Priva Blue ID Network scanner / Syslog Tool Priva Blue ID Network scanner / Syslog Tool Versie 1.3.15.0 Bladzijde 1 Inhoudsopgave Inhoudsopgave... 2 Introductie:... 3 Systeemeisen:... 4 Installeren op een SX100:... 5 De Werking:... 6 Scannen van

Nadere informatie

RS BA-1 settings Opgesteld door Rens, PA1RVL op 15-10-2012 1

RS BA-1 settings Opgesteld door Rens, PA1RVL op 15-10-2012 1 RS BA-1 settings Opgesteld door Rens, PA1RVL op 15-10-2012 1 INHOUD Inleiding 3 De basiscomputer 4 De remotecomputer 9 Het geheel in gebruik nemen 14 Errors 15 2 Inleiding De basiscomputer is de computer

Nadere informatie

Datatypes Een datatype is de sort van van een waarde van een variabele, veel gebruikte datatypes zijn: String, int, Bool, char en double.

Datatypes Een datatype is de sort van van een waarde van een variabele, veel gebruikte datatypes zijn: String, int, Bool, char en double. Algemeen C# Variabele Een variabele is een willekeurige waarde die word opgeslagen. Een variabele heeft altijd een datetype ( De soort waarde die een variabele bevat). Datatypes Een datatype is de sort

Nadere informatie

We moeten de accommodaties selecteren die 3 sterren hebben, en in land met ID 10 zitten.

We moeten de accommodaties selecteren die 3 sterren hebben, en in land met ID 10 zitten. MySQL talk Trage website? Het optimaliseren van een bestaande website die een MySQL database heeft is niet altijd even makkelijk. Het probleem kan namelijk op veel verschillende plekken zitten: de database

Nadere informatie

S u b n e t t e n. t h e t r u e s t o r y 1100 0000. 1010 1000. 0000 0001. 0000 0001 1111 1111. 1111 1111. 1111 1111. 0000 0000.

S u b n e t t e n. t h e t r u e s t o r y 1100 0000. 1010 1000. 0000 0001. 0000 0001 1111 1111. 1111 1111. 1111 1111. 0000 0000. S u b n e t t e n t h e t r u e s t o r y 1100 0000. 1010 1000. 0000 0001. 0000 0001 1111 1111. 1111 1111. 1111 1111. 0000 0000 Part 1 Inhoud Wat is een subnet?... 2 Waarom?... 3 Het begin.... 3 Een voorbeeld...

Nadere informatie

INHOUDSOPGAVE IMUIS INSTALLEREN 2 WINDOWS 2. WINDOWS SERVER 2008 r2 4 UITGAANDE VERBINDINGEN 5 INSTALLATIE IMUISONLINE.MSI 5 SSL CERTIFICAAT 5

INHOUDSOPGAVE IMUIS INSTALLEREN 2 WINDOWS 2. WINDOWS SERVER 2008 r2 4 UITGAANDE VERBINDINGEN 5 INSTALLATIE IMUISONLINE.MSI 5 SSL CERTIFICAAT 5 INHOUDSOPGAVE IMUIS INSTALLEREN 2 WINDOWS 2 WINDOWS SERVER 2008 r2 4 UITGAANDE VERBINDINGEN 5 INSTALLATIE IMUISONLINE.MSI 5 SSL CERTIFICAAT 5 STARTEN VAN IMUIS ONLINE 5 LINK VANAF UW WEBSITE 6 CONTACTGEGEVENS

Nadere informatie

Muziek downloaden MP3 WMA Liedjes of albums? Collectie Waar?

Muziek downloaden MP3 WMA Liedjes of albums? Collectie Waar? Muziek downloaden Muziek downloaden kan op verschillende manieren en bij verschillende diensten. Op deze pagina leggen we uit wat de mogelijkheden zijn. Formaten Verschillende download diensten bieden

Nadere informatie

INHOUDSOPGAVE IMUIS INSTALLEREN 2 WINDOWS 2. WINDOWS SERVER 2008 r2 3 UITGAANDE VERBINDINGEN 4 INSTALLATIE IMUISONLINE.MSI 4 SSL CERTIFICAAT 4

INHOUDSOPGAVE IMUIS INSTALLEREN 2 WINDOWS 2. WINDOWS SERVER 2008 r2 3 UITGAANDE VERBINDINGEN 4 INSTALLATIE IMUISONLINE.MSI 4 SSL CERTIFICAAT 4 INHOUDSOPGAVE IMUIS INSTALLEREN 2 WINDOWS 2 WINDOWS SERVER 2008 r2 3 UITGAANDE VERBINDINGEN 4 INSTALLATIE IMUISONLINE.MSI 4 SSL CERTIFICAAT 4 STARTEN VAN IMUIS ONLINE 4 LINK VANAF UW WEBSITE 5 CONTACTGEGEVENS

Nadere informatie

Laten we eens beginnen met de mouwen op te stropen en een netwerk te bouwen.

Laten we eens beginnen met de mouwen op te stropen en een netwerk te bouwen. Practicum Filius In deze proefles gaan we jullie kennis laten maken met computernetwerken. Na afloop van dit practicum heb je een goede basis van waar een netwerk uit kan bestaan, hoe je een netwerk bouwt

Nadere informatie

Technische architectuur Beschrijving

Technische architectuur Beschrijving A gemeente Eindhoven Technische architectuur Beschrijving Specificatiecriteria Versie 1.1 A. van Loenen Technisch Beleidsadviseur B&E 21-Sep-2011 avl/fd11027578 Colofon Uitgave Gemeente Eindhoven Realisatie

Nadere informatie

Xiris handleiding Onderhoudsmodule & database onderhoud

Xiris handleiding Onderhoudsmodule & database onderhoud Xiris handleiding Onderhoudsmodule & database onderhoud Copyright 2011 FP-Ruys. FP-Ruys kan geen aansprakelijkheid aanvaarden voor schade die het gevolg is van enig fout in deze handleiding of verkeerd

Nadere informatie

Handleiding aanmaak CSR

Handleiding aanmaak CSR Handleiding aanmaak CSR Voordat u begint: Om een Certificate Signing Request (CSR) te maken moet het programma OpenSSL geïnstalleerd worden. Dit programma kan geheel gratis gedownload worden vanaf de OpenSSL

Nadere informatie

Aan de slag met DNS Jeroen van Herwaarden, Robbert-Jan van Nugteren en Yannick Geerlings 19-3-2010

Aan de slag met DNS Jeroen van Herwaarden, Robbert-Jan van Nugteren en Yannick Geerlings 19-3-2010 Aan de slag met DNS Jeroen van Herwaarden, Robbert-Jan van Nugteren en Yannick Geerlings 19-3-2010 Inhoud Hoofdstuk 1 Inleiding... 3 Hoofdstuk 2 Algemene informatie over DNS... 4 Hoofdstuk 3 Verschillende

Nadere informatie

Computervaardigheden. Universiteit Antwerpen. Computervaardigheden en Programmatie. Grafieken en Rapporten 1. Inhoud. Wat is scripting?

Computervaardigheden. Universiteit Antwerpen. Computervaardigheden en Programmatie. Grafieken en Rapporten 1. Inhoud. Wat is scripting? Inhoud Computervaardigheden Hoofdstuk 4 Scripting (Let op: dit is enkel voor studenten Biologie.) Dit hoofdstuk bekijkt heel kort de basis van scripting. - Opstellen van functies. - Conditionele code.

Nadere informatie

Installatiehandleiding TiC Narrow Casting Manager

Installatiehandleiding TiC Narrow Casting Manager Installatiehandleiding TiC Narrow Casting Manager Inhoudsopgave 1. Algemeen - 3-2. Installatie PostgreSQL database server - 4-3. Installatie FTP server - 9-4. Aanmaken account in FileZilla server - 13

Nadere informatie

IdentySoft Basic Support Handleiding EasySecure International B.V.

IdentySoft Basic Support Handleiding EasySecure International B.V. IdentySoft Basic Support Handleiding EasySecure International B.V. +31(0)88 0000 083 Info@EasySecure.nl www.easysecure.nl Om onze dealers zo goed mogelijk bij te staan hebben wij het volgende document

Nadere informatie

Naam: Sander van Schie Datum: 28-03-2014 Klas: SBICO-IB2 Doel: Uitleg Toegang tot vcloud Doelgroep: Nieuwe cursisten Versie: 1.0.0

Naam: Sander van Schie Datum: 28-03-2014 Klas: SBICO-IB2 Doel: Uitleg Toegang tot vcloud Doelgroep: Nieuwe cursisten Versie: 1.0.0 Naam: Sander van Schie Datum: 28-03-2014 Klas: SBICO-IB2 Doel: Uitleg Toegang tot vcloud Doelgroep: Nieuwe cursisten Versie: 1.0.0 1 Inhoudsopgave Inleiding... 3 Stap 1: Inloggegevens en wachtwoord...

Nadere informatie

Welkom bij de HCC Haaglanden. Het is vandaag Dinsdag 11 mei 2010 Een presentatie over Geheugen, Torrent en ITunes

Welkom bij de HCC Haaglanden. Het is vandaag Dinsdag 11 mei 2010 Een presentatie over Geheugen, Torrent en ITunes Welkom bij de HCC Haaglanden Het is vandaag Dinsdag 11 mei 2010 Een presentatie over Geheugen, Torrent en ITunes Interngeheugen.com Een programmaatje wat je computer aftast wat er aan geheugen bijgeplaatst

Nadere informatie

Friesland College Leeuwarden

Friesland College Leeuwarden Voorwoord In dit project vertel ik wat Open Source is en wat ik daarover heb gevonden. Ik laat zien hoe ik een Virtuele machine geschikt maak voor Dual Boot. Dan laat ik zien hoe je 2 besturingssystemen

Nadere informatie

1. Kopieer de bestanden die in de html directory staan, naar de html directory van HomeSeer.

1. Kopieer de bestanden die in de html directory staan, naar de html directory van HomeSeer. Homeseer Integratie Voor de integratie van het JeeLabs platform in Homeseer maken we gebruik van een Open Source pakket genaamd JeeLink for HomeSeer (http://sourceforge.net/projects/jeeseer/) van Tijl

Nadere informatie

Klachtenbeheer (Intranet)

Klachtenbeheer (Intranet) Klachtenbeheer (Intranet) Versie:1 1/17 1 INLEIDING...3 2 NAVIGATIE VAN DE APPLICATIE...3 3 FRONT-END (OP DE WEBSITE)...4 3.1 Het online melden van klachten... 4 3.2 Mijn meldingen... 5 4 BACK-END...6

Nadere informatie

WebHare Professional en Enterprise

WebHare Professional en Enterprise WebHare Professional en Enterprise Publicatie module Site inrichting handleiding Datum 19 november 2002 Aantal pagina s: 31 Versie: 2.01 Doelgroep Sysops Gebruikers met site aanmaak rechten Gebruikers

Nadere informatie

Verslag. Projectteam: 107 Datum: 16 oktober 2008 Project leden: Lennard Fonteijn Harish Marhe Nicoletta Saba Turgay Saruhan Robin Tummers

Verslag. Projectteam: 107 Datum: 16 oktober 2008 Project leden: Lennard Fonteijn Harish Marhe Nicoletta Saba Turgay Saruhan Robin Tummers Verslag SE Projectteam: 107 Datum: 16 oktober 2008 Project leden: Lennard Fonteijn Harish Marhe Nicoletta Saba Turgay Saruhan Robin Tummers In dit verslag zullen wij een beschrijving geven, over welke

Nadere informatie

Werkinstructie. Technisch Beheer. uitvoeren MAP scan. voor. Datum: 15 oktober Versie: 1.0

Werkinstructie. Technisch Beheer. uitvoeren MAP scan. voor. Datum: 15 oktober Versie: 1.0 uitvoeren MAP scan voor Technisch Beheer Auteur: ValueBlue Datum: 15 oktober 2016 Versie: 1.0 uitvoeren MAP scan Page 2 / 22 Revisie status Versie Datum Naam Reden Veranderde items 1.0 15-10-2016 ValueBlue

Nadere informatie

Handleiding Office 2013 en Office 365. voor thuisgebruik

Handleiding Office 2013 en Office 365. voor thuisgebruik Handleiding Office 2013 en Office 365 voor thuisgebruik Versie: augustus 2015 1 Inhoudsopgave 1. Wat is Office 365 en wat kun je ermee?... 3 2. Wat je moet weten voordat je gaat installeren?.... 13 3.

Nadere informatie

Handleiding helpdesk. Datum: 08-10-2014 Versie: 1.0 Auteur: Inge van Sark

Handleiding helpdesk. Datum: 08-10-2014 Versie: 1.0 Auteur: Inge van Sark Datum: 08-10-2014 Versie: 1.0 Auteur: Inge van Sark Inhoudsopgave Inhoudsopgave... 2 1. Beheer helpdesk... 3 1.1. Settings... 3 1.2. Applicaties... 4 1.3. Prioriteiten... 5 1.4. Gebruik mailtemplates...

Nadere informatie

De Kleine WordPress Handleiding

De Kleine WordPress Handleiding Introductie Dit is geen uitgebreide handleiding om een WordPress website of blog mee te bouwen. Het is ook geen overzicht van alle aspecten die een WordPress website zo bijzonder maken en geen verhandeling

Nadere informatie

Help! Hoe installeer ik correct mijn nieuwe NAS server?

Help! Hoe installeer ik correct mijn nieuwe NAS server? Help! Hoe installeer ik correct mijn nieuwe NAS server? Als U nieuwe harde schijf(ven) heeft aangekocht, dan zijn deze maagdelijk blank. Geen enkel besturingssysteem kan met deze harde schijven zo aan

Nadere informatie

Installatie SQL: Server 2008R2

Installatie SQL: Server 2008R2 Installatie SQL: Server 2008R2 Download de SQL Server 2008.exe van onze site: www.2work.nl Ga naar het tabblad: Downloads en meld aan met: klant2work en als wachtwoord: xs4customer Let op! Indien u een

Nadere informatie

ICT. Bevoegdheden in een Windows thuisnetwerk. Een praktische snelgids. Het Rocht HJ VRIES internet:

ICT. Bevoegdheden in een Windows thuisnetwerk. Een praktische snelgids. Het Rocht HJ VRIES internet: HaarmanB ICT Het Rocht 65 9481 HJ VRIES 0592-580170 internet: www.haarmanb-ict.nl e-mail: support@haarmanb-ict.nl Kvk te Meppel: 04072901 Bevoegdheden in een Windows thuisnetwerk Een praktische snelgids

Nadere informatie

Bijlage Inlezen nieuwe tarieven per verzekeraar

Bijlage Inlezen nieuwe tarieven per verzekeraar ! Bijlage inlezen nieuwe tarieven (vanaf 3.2) Bijlage Inlezen nieuwe tarieven per verzekeraar Scipio 3.303 biedt ondersteuning om gebruikers alle tarieven van de verschillende verzekeraars in één keer

Nadere informatie

Sick Beard installeren en configureren voor gebruik in combinatie met SABnzbd+...

Sick Beard installeren en configureren voor gebruik in combinatie met SABnzbd+... pagina 1 van 6 Sick Beard installeren en configureren voor gebruik in combinatie met SABnzbd+ (Windows) Introductie Sick Beard is een stukje software wat heel wat werk uit handen kan nemen. Het weet welke

Nadere informatie