Java Magazine #1 - 2013

Page 1

mei/juni 2013 nr. 1

MAGAZINE Onafhankelijk tijdschrift voor de Java-professional

Java Magazine is

Volledige

TERUG

samenwerking

met DevOps Features Bouw je eigen robot cover.indd 1

Spoedcursus MongoDB

Apache Camel: voor complete integratie 5/1/13 2:17 PM


OOK HET IDEE DAT JE IETS HEBT GEMIST?

Ordina feliciteert alle Javanen met de terugkeer van hun lijfblad. Zonder lijfblad ben je als Javaan niet compleet. Geweldig dus dat JAVA Magazine na anderhalf jaar weer van de persen rolt. Daarmee is de inhoud terug die je zo hebt gemist. Dat diepgaande artikel. Het internationale nieuws. Die handige tip. Alles wat je vak boeiend maakt. Geef je lijfblad nĂłg meer body! Wat mag volgens jou echt niet ontbreken in het nieuwe JAVA Magazine? Deel het met allemaal. En wie weet staat jouw bijdrage in het volgende nummer. Twitter je ideeĂŤn naar #mijnjava

ICT MAAR DAN VOOR MENSEN werkenbijordina.nl javaman.indd 2 Untitled-1 1

25-04-13 16:19 29-4-2013 9:58:21


VOORWOORD

Java Magazine is terug! Zoals de kop van dit voorwoord ook al zegt: Java Magazine is terug! Na een tijdje in print afwezig te zijn geweest, heb je nu dan eindelijk de hernieuwde en gerestylde editie van Java Magazine in handen. Ik had nu graag gewild dat bij het moment van open slaan het intro van “Eye of the Tiger” of de soundtrack van Rocky, zou klinken. Deze muziek illustreert namelijk exact ons enthousiasme over Java Magazine en de NLJUG community. Iedereen heeft er heel veel zin in om alle leden van NLJUG weer te voorzien van de nodige kennis en kunde op Java-gebied middels het Java Magazine. Dit enthousiasme wordt vooral aangewakkerd door de grote betrokkenheid van de vele NLJUG-leden, die hielpen bij het realiseren van Java Magazine. Naast het nieuwe magazine ook een nieuwe content manager. Inderdaad, ik noem mijzelf geen hoofdredacteur. Dit omdat het Java Magazine maar slechts een onderdeel is van een totaal hernieuwd aanbod aan diensten richting onze leden. Zo zullen ook de vertrouwde NLJUG Univerisity, de J Fall en de nieuwe nljug.org website [inclusief javamagazine info] onderdeel gaan uitmaken van mijn verantwoordelijkheden. Ik heb er zin in om samen met de leden, de programmacommissie, het bestuur en de redactieraad te werken aan toepasselijke info die jou als Java professional kan ondersteunen. Het magazine is daarvan de eerste output. Wij [de redactieraad en ik] hopen dan ook echt dat deze uitgave jouw die ondersteuning dan ook weer kan bieden. Wil je zelf ook bijdragen door iets te schrijven voor het magazine? Twijfel niet en neem contact met ons op! NLJUG bestaat immers door en voor de leden. Ik wens je in ieder geval veel leesplezier met het vernieuwde Java Magazine en hopelijk slaat ons enthousiasme ook op jou over! Ramon Wieleman Content manager Java Magazine

Colofon Java Magazine 01-2013 Content manager: Ramon Wieleman Eindredactie: Lilian Krijt Auteurs: Joop Lanting, Maikel Alderhout, Robert Scholte, Hubert ten Hove, Eelco Meuter, Remko de Jong, Marco Hebing, Anko Tijman, Frank Verbruggen, Eric-jan Malotaux, Ben Ooms, Bert Ertman, Frans van der Lek, Bert Jan Schrijver, Nanne Baars, Peter Hendriks Redactiecommissie: Nanne Baars, Eelco Meuter, Bert Jan Schrijver, Ben Ooms, Bert Ertman, Lucas Jellema, Tim Prijn Vormgeving: Britt Zaal, Jannemieke Duyzer, Jan Wesel Uitgever: Martin Smelt Traffic: Marco Verhoog Media order: Mirella van der Willik Abonnementenadministratie: Ingrid van der Aar, Tanja Ekel, Brigitte Hetem Drukkerij: Senefelder Misset, Doetinchem Distributie: Betapress Sales: Richelle Bussenius E-mail: r.bussenius@hub.nl Telefoon: 023 752 39 22 Fax: 023 535 96 27 Java Magazine verschijnt 4 maal per jaar en is onderdeel van het lidmaatschap NLJUG en kost € 39,50 (België € 40,50) per jaar. Naast het Java Magazine krijgt u gratis toegang tot de vele NLJUG workshops en het JFALL congres. Het NLJUG is lid van het wereldwijde netwerk van JAVA user groups. Voor meer informatie of wilt u lid worden, zie www.nljug.org. Een nieuw lidmaatschap wordt gestart met de eerst mogelijke editie voor een bepaalde duur. Het lidmaatschap zal na de eerste (betalings)periode stilzwijgend worden omgezet naar lidmaatschap van onbepaalde duur, tenzij u uiterlijk één maand voor afloop van het initiële lidmaatschap schriftelijk (per brief of mail) opzegt. Na de omzetting voor onbepaalde duur kan op ieder moment schriftelijk worden opgezegd per wettelijk voorgeschreven termijn van 3 maanden. Een lidmaatschap is alleen mogelijk in Nederland en België. U kunt mailen naar members@nljug.org of schrijven naar NLJUG BV, ledenadministratie, postbus 3389, 2001 DJ Haarlem. Op werkdagen kunt u bellen tussen 10 en 14 uur naar telefoonnummer 023–5364401. Verhuisberichten of bezorgklachten kunt u doorgeven via www.hubstore.nl (Klantenservice).

JAVA MAGAZINE | 01 2013

Voorwoord.indd 3

5/1/13 2:19 PM


#xebicon of volg @xebia

04

WIL JIJ IN 4 UUR ALLES TE WETEN KOMEN OVER DE LAATSTE IT TRENDS? Kom dan op dinsdag 4 juni om 15:00 uur naar XebiCon in Fort Voordorp. XebiCon brengt je helemaal up-to-date op het gebied van Agile, Big Data, Continuous Delivery, DevOps, Mobile en Single Page Webapps!

XebiCon heeft ook dit jaar een spraakmakend programma met nationale en internationale sprekers. De conferentie start spectaculair met keynote speaker en extreem avonturier, Marc Sluszny. Zijn motto is:

Verwezenlijk je dromen, verleg je grenzen, overwin je angsten.

De conferentie zal worden afgesloten met de Agile Troublemaker en bedenker van Behavior Driven Development, Dan North! Voor slechts â‚Ź 50,- incl. BTW kun je deelnemen aan XebiCon. Op xebicon.nl vind je meer informatie over het programma en de locatie. Schrijf je snel in, want het aantal plaatsen is beperkt. Graag tot ziens op XebiCon 2013!

Registratie op www.xebicon.nl Op www.xebicon.nl vind je ook informatie over het programma en de locatie.

WORDT GEORGANISEERD DOOR XEBIA

Xebicon 2013 adv-1.indd 1

26-04-13 08:47 mei/juni 2013 nr. 1

MAGAZINE Onafhankelijk tijdschrift voor de Java-professional

Java Magazine is

Volledige

TERUG

samenwerking

met DevOps

Voordelen van lidmaatschap Gratis toegang tot NLJUG events Gratis toegang tot NLJUG University sessions

Features Bouw je eigen robot

Voor elke serieuze Java-ontwikkelaar en/ of -bedrijf werkend met Java-technologie is het een must om lid te worden van de NLJUG. Als u up-to-date wilt zijn en blijven is dit de manier om het te doen: word lid van de NLJUG op www.nljug.org. Het lidmaatschap kost â‚Ź 39,- (incl. 6% BTW)

Spoedcursus MongoDB

Apache Camel: voor complete integratie

Gratis 4 maal per jaar het Java Magazine

JAVA MAGAZINE

Werf.indd 4

5/1/13 2:22 PM


INHOUDSOPGAVE

DevOps, de ‘zachte’ kant DevOps is een samenvoeging van development en operations. Met DevOps is er samenwerking mogelijk tussen ontwikkeling en beheer om continuous delivery in een organisatie te laten slagen. DevOps implementeren in een organisatie kent ‘harde’ en ‘zachte’ aspecten. Java Magazine verkende de zachte aspecten van een DevOps-implementatietraject aan de hand van eigen ervaringen.

12

Effectieve EAI met Apache Camel

16

Requirements in IT-projecten en requirements aan de integratie met andere systemen zijn gevoelig voor verandering. Snel reageren op deze veranderingen kan cruciaal zijn voor een succesvol project. Enterprise Application Integration (EAI) biedt alle kennis, technologie en best practices, om uitbreidbare en onderhoudbare integratie oplossingen te bouwen op een productieve manier. In dit artikel zal Apache Camel gebruikt worden om te kunnen voldoen aan complexe integratie vraagstukken.

06

SPRING CONFIGURATIE Met @Configuration

10

BOEKBESPREKING Java Application Architecture

11

COLUMN Zieners

12

DEVOPS Het gehele implementatietraject

16

APACHE CAMEL Effectieve EAI

22

MONGODB De database van de toekomst?

27

LIBRARIES Libraries uitgelicht

28

RASPBERRY PI Ja-Pi, de eerste mobiele enterprise-server

32

GRAFISCHE USER INTERFACE Met Java en Sikuli

36

BESTUURSCOLUMN NLJUG Van het bestuur

38

TIPS & TRICKS Meer met Maven

SCHRIJVEN VOOR JAVA MAGAZINE?

28

Ben je een enthousiast lid van NLJUG en zou je graag willen bijdragen aan Java Magazine? Of ben je werkzaam in de IT en zou je vanuit je functie graag je kennis willen delen met de NLJUG-community? Dat kan! Neem contact op met de redactie, leg uit op welk gebied je expertise ligt en over welk onderwerp je graag zou willen schrijven. Direct artikelen inleveren mag ook. Mail naar info@nljug.org en wij nemen zo spoedig mogelijk contact met je op.

Ja-Pi, de eerste mobiele enterprise-server Enkele leden van de redactiecommissie zijn gek van de Raspberry Pi en wilden wel eens kijken wat er allemaal mogelijk is met deze mini-computer. Java Magazine zocht uit hoe je met Java enkele led’s via een website kunt aansturen. Daarnaast heeft Ja-Pi, de eerste mobiele Java enterprise-server, het levenslicht gezien. Alles is stap voor stap uitgelegd, zodat je thuis ook zelf aan de slag kan gaan met Raspberry Pi.

JAVA MAGAZINE | 01 2013

Inhoud.indd 5

5/1/13 2:25 PM


06

Spring configuratie met @Configuration Spring configureren via een geannoteerde Java klasse. Voor de één een mooie oplossing en voor de ander is het een gruwel. Dit artikel gaat niet over deze discussie, maar over de werking en mogelijkheden van het configureren van de Spring container door middel van de @Configuration annotatie.

Dit artikel is gebaseerd op ervaringen met het gebruik hiervan en op bestudering van de javadocs van de besproken annotaties en classes. Doel is dan ook niet om volledig te zijn, daar biedt Spring voldoende documentatie voor. Het doel is om een eenvoudig overzicht te hebben van de mogelijkheden, punten waar op te letten en mogelijk toepassing van de functionaliteit. Ook zullen we belichten hoe een Spring container omgaat met een mix van XML en op annotaties gebaseerde configuratie. Als laatste tonen we hoe in een webcontext de Spring container te instantiëren wanneer je een geannoteerde configuratie hebt. De @Configuration annotatie biedt de mogelijkheid om naast of zonder de reguliere XML configuratie een simpele Java klasse te annoteren met @Configuration. In deze Java klasse kun je de Beans configureren zoals je in XML zou doen. Deze geannoteerde Java klasse is zelf gewoon een Bean zoals een @Component en kan als zodanig gebruikt worden. Sinds Spring 3.1 zijn er geen beperkingen tegenover XML configuratie, alles wat je in XML kunt definiëren, kun je dus ook in een Java klasse annoteren met @Configuration. Enkele voordelen van het configureren op deze manier zijn onder andere dat de configuratie door middel van Java geschreven is, mogelijke fouten eerder zichtbaar zijn (tijdens compileren), het mogelijk is Beans van een anonymous inner type te declareren en het conditioneel creëren van een Bean. De container kan prima overweg met een mix van XML

en geannoteerde configuratie, het ene sluit het andere niet uit.

Ben Ooms is een gepassioneerde Java senior software engineer werkzaam bij Sogeti.

Om Java configuratie te kunnen gebruiken, moet naast de reguliere Spring afhankelijkheden ook CGlib toegevoegd worden wanneer je een Spring versie onder 3.2 gebruikt. Dit komt omdat CGlib door Spring als een optionele afhankelijkheid gedefinieerd is. Naast Java configuratie wordt CGlib ook door Spring AOP gebruikt. De reden waarvoor CGlib noodzakelijk is, is omdat de @Configuration geannoteerde klasse een gewone bean is die daarnaast ook de context, scope en Bean semantiek moet weten. Hiervoor maakt Spring een CGlib proxy voor elke @Configuration Bean aan. Vanaf Spring 3.2 is deze afhankelijkheid niet meer nodig, en is de CGlib code van versie 3.0 geïntegreerd in Spring en te vinden in de org. springframework.cglib package. Doordat CGlib tijdens het opstarten dynamisch functionaliteit toevoegt, zijn er twee eisen waar een @Configuration geannoteerde klasse aan moet voldoen: de klasse mag niet final zijn en er moet een default no args constructor zijn. De simpelste form kan er zo uitzien: Package com.foo.config; @Configuration public class MyConfig{ public MyConfig(){} @Bean coffeeBean(){ Return new CoffeeBean(); } }

JAVA MAGAZINE

Spring2kolom.indd 6

5/1/13 2:31 PM


SPRING CONFIGURATIE

In XML zou de configuratie er zo uitzien: <beans>

<bean id=”coffeBean” class=”com.foo.config.CoffeBean” > </> </ beans>

Om deze configuratie te laden in een XML geconfigureerde container, moet de welbekende (als je al met Spring annotaties werkt) context:component-scan of de context:annotation-config tag aanwezig zijn. Zoals je misschien al verwacht, is een @ Configuration geannoteerde klasse inderdaad gewoon een bean zoals een @Component of soortgelijke geannoteerde klasse. Als je met de context:component-scan wil werken is dit in je XML voldoende: <context:component-scan />

Zoals getoond in bovenstaand voorbeeld, kun je in een bestaande XML gebaseerde Spring configuratie door middel van de context:component-scan Java gebaseerde configuratie toevoegen. Wil of kan je de component-scan niet gebruiken, dan is dat ook mogelijk door middel van de context: annotation-config en voeg je deze tag toe in je XML. <context:annotation-config />

Daarnaast is het ook mogelijk om alleen op annotatie gebaseerde configuratie in te laden. Spring heeft hiervoor een tweetal bootstrap classes. De AnnotationConfigApplicationContext en de web versie AnnotationConfigWebApplicationContext. De AnnotationConfigApplicationContext kan op drie manieren geïnstantieerd worden. De eerste manier lijkt op de manier zoals je dit met een XML gebaseerde Spring configuratie zou doen: AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Configuratie.class);

De tweede manier gebruikt de default constructor en het scanning mechanisme: AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.scan(“de.package.die.je.wil.scannen”); ctx.refresh();

Als laatste de manier die erg op de tweede manier lijkt maar de register methode gebruikt om de container te voorzien van de configuratie: AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ConfigA.class); ctx.register(ConfigB.class); ctx.refresh();

De AnnotationConfigWebApplicationContext is op dezelfde manieren te instantiëren als de XML variant. De op annotaties gebaseerde configuratie heeft twee annotaties om andere configuraties in te laden. Als je een andere Java geannoteerde configuratie wilt inladen, gebruik je de @Import annotatie. Met @ImportResource kun je een XML configuratie inladen. Tijdens het schrijven van de voorbeeld code, kwam ik er achter dat je hiermee moet uitkijken wanneer je meer dan een @Configuration geannoteerde klasse heb. Doordat een Java klasse met de @Configuration onderhuids ook een gewone Bean is, kun je een andere configuratie klasse ook door middel van @Autowired injecteren. Dit heeft als voordeel dat het meteen duidelijk is waar de gebruikte Bean geconfigureerd wordt. Het nadeel is echter dat hierdoor de beide configuratie klassen tightly coupled zijn. Een voorbeeld hiervan is bijvoorbeeld: Gegeven een xml configuratie genaamd springapp.xml @Configuration @ImportResource(“springapp.xml”) public class MyConfig{ rest van de klasse...

Externe properties kunnen geladen worden door middel van de @PropertySource annotatie of door middel van de @Value annotatie met een geconfigureerde propertyPlaceholderConfigurator. Als je de @PropertySource manier gebruikt, moet je ook een Spring Environment laden door middel van bijvoorbeeld @Autowired in de configuratie klasse. Daarna kun je de property inlezen met de getProperty(‘naam”) van de geïnjecteerde Environment klasse. Belangrijk om te weten is dat wanneer twee ingeladen properties bestanden dezelfde property bevatten, de waarde van de laatst geladen properties bestand gebruikt wordt en dat de properties alleen binnen een @Bean definitie te gebruiken zijn.

JAVA MAGAZINE | 01 2013

Spring2kolom.indd 7

5/1/13 2:31 PM


08

Gegeven een property file genaamd props.properties met een key genaamd propKey @Configuration @propertySource(“props.properties”) public class MyConfig{ @Autowired Environment env @Bean InjectedBean injectedBean(){ InjectedBean b = new InjectedBean(); b.setProp(env.getProperty(“propKey”)); return b; } rest van de klasse...

Met de PropertyPlaceholderConfigurator heb je twee keuzes. Als deze al in een XML gedefinieerd wordt, kun je direct aan de slag met de @Value annotatie om properties in te lezen. Mocht die er niet zijn, dan kun je deze ook instantiëren in de Java configuratie klasse. Om dit te doen moet je op klasse niveau door middel van de @PropertySource annotatie de locatie van het properties bestand, een PropertyPlaceholderConfigurator bean definiëren en properties inlezen met @Value. Sinds Spring 3.1 kun je verschillende profielen gebruiken. Met een profile creëer je een logisch bij elkaar horende Bean verzameling. Deze verzameling wordt alleen geladen wanneer het profiel als actief wordt aangemerkt. Het activeren van een profile kan door middel van het toevoegen van een System property genaamd spring.profiles.active met als waarde een of meerdere profiel namen, of door de methode setActiveProfile aan te roepen. Vanaf Spring 3.1 is voor de XML versie aan de beans tag het attribuut profile gekomen en in een op basis van annotaties geconfigureerde container kun je de @Profile annotatie toevoegen. Interessant aan de profielen is dat je deze runtime kan laden. Zoals je kunt zien is de flexibiliteit erg groot

@Configuration @profile(“st”) public class MyConfig{ rest van de klasse... // Profiel laden in context ApplicationContext ctx = new annotationConfigContext(); ctx.setActiveProfile{“st”); ctx.refresh();

en kun je een Java configuratie op veel verschillende manieren gebruiken. Je kunt bijvoorbeeld binnen een bestaande XML geconfigureerde container met de component-scan of annotation-config Java configuratie toevoegen, of een bestaande XML gebaseerde container ombouwen tot een op annotatie gebaseerd container door gebruik te maken van de @ImportResource om zodoende een geleidelijke transitie te kunnen maken. Na het maken van de voorbeeld code ben ik er achter gekomen dat hoewel een mix tussen XML en geannoteerde goed te doen is het de onderhoudbaarheid niet ten goede komt. Zeker wanneer je Spring profielen, XML en geannoteerde configuratie combineert, moet je de geconfigureerde container goed testen op de aanwezigheid van de door jou verwachte Beans.

HET PROJECT KUN JE DIRECT GEBRUIKEN ALS EEN SPRING SANDBOX OM MET SPRING TE EXPERIMENTEREN.

Beans zijn te definiëren door de @Bean annotatie toe te voegen aan een methode. Het interessante van een Bean annotatie is dat deze ook gedeclareerd kunnen worden in een niet @Configuration geannoteerde klasse. Let er wel op dat als de Beans gedeclareerd worden in een klasse die niet de annotatie @Configuration heeft, de scope niet gerespecteerd wordt. De optionele attributen van de @Bean annotatie zijn name, autowire, initmethod en destroymethod. Als er geen specifieke naam gegeven is aan de bean zal de naam

JAVA MAGAZINE

Spring2kolom.indd 8

5/1/13 2:31 PM


SPRING CONFIGURATIE

hetzelfde zijn als de method. Dit is te veranderen door het attribuut name toe te voegen. Interessant is dat de name attribuut een array van Strings accepteert om dus meerdere aliassen toe te kennen aan de Bean. Met het autowire attribuut geeft men aan of de bean attributen automatisch geinjecteerd moeten worden. Standaard staat dit uit. De mogelijke andere waarden zijn net als bij een XML geconfigureerde container injectie door middel van bean type of naam. Met de init methode parameter kun je aangeven welke methode aangeroepen moet worden na instantiëring van de bean. De bruikbaarheid hiervan is discutabel, want hetzelfde kan worden gedaan door in de Bean methode body de bewuste methode aan te roepen op het object. De destroy method parameter is wel iets bruikbaarder. Hiermee kun je, zoals de naam al aanduidt, aangeven welke method moet worden aangeroepen wanneer de Bean gedestroyed wordt. Als je deze parameter niet meegeeft, gaat de Spring container standaard bij het destroyen op zoek naar een publieke no arg methode close() en voert het deze uit. Deze standaard kun je overrulen door een lege String destroy methode naam mee te geven. Wanneer je dit doet zal de aanwezige

close methode niet aangeroepen worden. In de javadoc staat een specifieke uitzondering waar je op moet letten wanneer je een Bean definieert die een BeanFactoryPostProcessor type retourneert. Omdat deze Beans meestal zeer vroeg in de container lifecycle benodigd zijn, kunnen ze storend werken bij het verder instantiëren van andere Beans. Het advies is dan ook om deze type Beans als static te definiëren. Mocht de Spring container deze type Beans instantiëren vanuit een methode die niet static is, dan zal er een waarschuwing in de log vermeld worden. Nadeel is echter dat deze Beans niet voor Bean scoping of AOP in aanmerking komen wanneer ze door middel van een static methode zijn geïnstantieerd. Ik hoop dat je met dit artikel een goede indruk hebt gekregen van de mogelijkheden die beschikbaar zijn wanneer je Spring wil gebruiken met een configuratie door middel van annotaties. Op bitbucket kun je een maven eclipse project uitchecken op https:// bitbucket.org/bendh/spring-configurationsandbox waarbij de in dit artikel besproken mogelijkheden gedemonstreerd worden. Het project kun je direct gebruiken als een Spring sandbox om met Spring te experimenteren.

Advertentie

Ben jij ook een Champions League speler

?

Kijk voor onze vacatures op pagina 20/21 van dit magazine.

JAVA MAGAZINE | 01 2013

Spring2kolom.indd 9

5/1/13 2:31 PM


10

BOEKBESPREKING

Boekbespreking Java Application Architecture

Eric Jan Malotaux is werkzaam bij Ordina als software architect. In de Java-wereld hebben we een stroom aan boeken over hoe je moet programmeren op de vierkante centimeter: wanneer je interfaces moet gebruiken in plaats van concrete ‘classes’, hoe groot methods moeten zijn, over Dependency Injection, Visitors, Observers, Model-View-Controller en honderd andere patterns. Javaontwikkelaars weten veel over het verdelen van een applicatie in logische lagen, of wanneer je een gelaagde applicatie juist beter kunt vermijden met bijvoorbeeld het CQRS (Command Query Responsibility Segregation) pattern. Er is ook genoeg geschreven over het definiëren van services en hoe je die moet groeperen in applicaties. Maar over het niveau daartussenin, hoe je een grote applicatie verdeelt in fysieke modules, is er gek genoeg verbazend weinig literatuur.‘Java Application Architecture: Modularity Patterns with Examples Using OSGi’ van Kirk Knoernschild is zo’n boek. Het richt zich op fysiek ontwerp zoals hoe een software systeem verpakt wordt in installeerbare eenheden, welke ‘classes’ in welke module thuishoren en welke afhankelijkheidrelaties er tussen de modulen bestaan. Het boek is verdeeld in drie delen. Deel I definieert wat een module is (zeg maar een jar-file), welke aspecten daaraan zitten, de plaats van modules in de applicatiearchitectuur, de functie van modulariteit bij het beperken van complexiteit en hoe modulariteit hergebruik van code kan bevorderen. Het eerste deel sluit af met een uitgewerkt

voorbeeld, waarin een eenvoudige maar monolithische applicatie in zeven stappen modulair wordt gemaakt. Deel II is een catalogus van achttien patterns, verdeeld in de categorieën base patterns, dependency patterns, usability patterns, extensibility patterns en utility patterns. Deel III tenslotte presenteert OSGi als het de facto enige beschikbare dynamische modulesysteem voor Java. Het beschrijft hoe je de in het voorgaande deel gemodulariseerde applicatie in een OSGi-omgeving kunt installeren, zodat de voordelen van een modulaire applicatie ook bij installatie en op runtime gerealiseerd kunnen worden. De achttien ‘Patterns Of Modular Architecture’ (POMA) in deel II vormen de kern van dit boek. Ze zijn beschreven op een manier die onafhankelijk is van Java of OSGi. Daardoor zijn ze breed toepasbaar in allerlei omgevingen, ook bijvoorbeeld .NET, al zal een .NETontwikkelaar waarschijnlijk afgeschrikt worden door het woord ‘Java’ in de titel. Hoewel OSGi al veel langer intern wordt toegepast in applicatieservers, is het pas bij recente versies mogelijk om daarop ook OSGi-applicatiebundles te installeren. En die versies hebben nog lang niet overal hun weg gevonden naar de productieomgevingen. Toch is het nuttig om de patterns ook nu al toe te passen, bijvoorbeeld op de verdeling van een systeem in maven modules. Wanneer OSGi (of Jigsaw) dan beschikbaar komt, zal het weinig moeite kosten om de applicatie daarvoor geschikt te maken. Opvallend genoeg maakt de auteur bij het bouwen van de modules in de voorbeelden gebruik van ant als build tool, terwijl veel Java-ontwikkelaars jaren geleden al ant verwisseld hebben voor maven. Het waarom daarvan wordt duidelijk, wanneer Knoernschild laat zien dat je voor het refactoren van

de modulestructuur van een applicatie alleen maar het ant build-script hoeft aan te passen. In maven is dat veel moeilijker. Daar zijn source files gegroepeerd naar modules. Bij refactoring van de modulestructuur betekent dit dat je de source files moet verplaatsen, ook in de versiebeheer repository, wat refactoring veel moeilijker maakt. Robert C. Martin schrijft in het voorwoord dat dit boek alleen ‘hard core stuff’ bevat en geen ‘fluff’. Dat is naar mijn mening niet helemaal waar, want de auteur gebruikt vrij veel woorden om te zeggen wat hij wilt en herhaalt zichzelf vaak. Dat doet overigens weinig af aan de waarde van het boek. Vooral de patterns van modulariteit zijn onschatbaar en verdienen het om grondig bestudeerd en misschien wel uit het hoofd geleerd te worden.

REFERENTIE

Java Application Architecture: Modularity Patterns with Examples Using OSGi / Kirk Knoernschild. - Prentice Hall, Upper Saddle River, NJ, 2012.

JAVA MAGAZINE

Boekreview.indd 10

5/1/13 2:35 PM


COLUMN

Zieners Zieners zijn mensen die zien wat wij niet zien. Net zoiets als paragnosten, maar dan beperkt tot toekomstige ontwikkelingen. En even onbruikbaar. Toch zijn er hele volksstammen die in ze geloven, ja, ook in dit ontwikkelde gebied. Ze kopen of verkopen direct de aangewezen goederen of aandelen, kiezen of versmaden de geduide contacten of negeren, dan wel storten zich op de voorspelde routes. Hoe het werkt? Nou, zet 6 mensen op een rij, laat ze elk een nummer kiezen uit 1-6, gooi met een dobbelsteen en er is er altijd een die BINGO roept. Herhaal dat een paar keer in het groot en je hebt trouwe volgelingen. Een en ander is ooit door Hitchcock verfilmd! Mijn vader had ooit een boek dat heette ‘Auto, Boot en Vliegtuig’ (jammer, het is zoek geraakt), dat vol met voorspellingen stond m.b.t. het ‘toekomstig’ verkeer. Gelukkig stamde het uit de dertiger jaren, dus als kleutertje kon ik zelf al vaststellen dat het allemaal onzin was. Sindsdien heb ik heel wat neergepende fantasieën gelezen en ik ben er inmiddels aan gewend geraakt dat de boeiendste verhalen de plank het hardst misslaan. Mooi voorbeeld: ‘De reis naar de maan’ van Jules Verne: helemaal gebaseerd op een door hem gelezen, maar totaal verkeerd begrepen, artikel in een wetenschappelijk tijdschrift. En in de automatiseringswereld? Dat is toch een gemeenschap van nuchtere lieden? Tja... < In 1966 ging ik, vers van de middelbare school, een cursus volgen bij IBM. Op de laatste dag, na een examentje, kregen we nog een demonstratie van rekenkracht te zien, waarbij de rondleider trots verkondigde dat “programmeren nu niet meer nodig was: er was nu RPG”. Gewoon een formuliertje invullen en de resultaten rolden uit de regeldrukker. Je moet er niet aan denken anno 2013. < In 1983 had ik mij tegoed gedaan aan COBOL, FORTRAN, ALGOL en C. Maar het was allemaal verloren tijd want “het Amerikaanse D.o.D. had een nieuwe alles vervangende taal uitgebracht: ADA”. Ja, het ministerie van oorlog deed ook nuttige dingen. Ik moet toegeven dat het nog schijnt te bestaan, ik zou alleen niet weten waar.

< In 1988 hoorde ik van mijn werkgever dat

alle (ALLE!) terminals en computers gingen worden vervangen door een aantal CASE workstations: teken een paar stroomdiagrammen en klaar. Kort daarna stonden we wel voor een faillissement. Ik wist nog net op tijd weg te komen ... < In 1997 ging ik zelf in de fout: op een NLUUG congres werd JAVA geïntroduceerd. Mijn gebruikelijk congresverslag vermeldde o.a. dat “JAVA niets nieuws bracht sinds PASCAL” en “meer een politiek dan technisch item was in de strijd tussen SUN en MICROSOFT”. Drie jaar later schreef, dacht en was ik JAVA; tot op de dag van heden. < En dan het allerergste wat me ooit is overkomen: DE RETRO-VISIONAIR: In 1971 stormde een technicus het lab waar ik werkte binnen met “weg met alle disks, er komt nu TUBE-MEMORY”: data bits op de voorkant van een TV-buis, geschreven en gelezen met de lichtsnelheid. Niets meer van vernomen, tot... In 1979 bezocht ik het Science Museum in Londen (Kensington), een museum waar je weken in kunt ronddwalen. Op de 4e verdieping van het middenschip is het automatiseringsmuseum. En wat glimlachte mij daar vanachter de vitrine ruit toe? TUBE-MEMORY by Williams, Ferranti 1954! Ik voelde me even als een archeoloog die een afbeelding van een iPhone tussen de hiërogliefen ziet. ;JOOP!

Joop Lanting, Amsterdam 1947. Gepensioneerd systeemprogrammeur, een der eersten in Europa met UNIX (1977), was onder meer werkzaam aan de Universiteit van Amsterdam, MultiHouse en Unisys.

TUBE-MEMORY by Williams, Ferranti 1954.

JAVA MAGAZINE | 01 2013

Column Zieners.indd 11

5/1/13 2:35 PM


12

Aan de slag met DevOps De ‘zachte’ kant belicht

‘DevOps’, een samentrekking van development en operations, is het logische vervolg van Agile: een samenwerking tussen ontwikkeling en beheer om continuous delivery in een organisatie te laten slagen. DevOps implementeren in een organisatie kent ‘harde’ en ‘zachte’ aspecten. In dit artikel verkennen we de zachte aspecten van een DevOps-implementatietraject aan de hand van onze eigen ervaringen.

Wij kunnen binnen de Java-wereld de DevOps-belofte laten slagen met behulp van continuous integration tools zoals Jenkins en Maven, met kwaliteitstools zoals JUnit, Selenium en FitNesse en met nieuwe continuous deployment tools en plugins zoals DeployIt, Build Pipeline en Puppet. Op technisch gebied zijn we er klaar voor, maar is dat genoeg? Om een nieuwe beweging in een organisatie te laten landen, is er meer nodig dan alleen de techniek op orde te hebben. Het is goed om vanuit verschillende perspectieven naar de implementatie van DevOps in de organisatie te kijken. De zeven aspecten hiervan komen terug in het honingraatmodel (figuur 1) dat wij hanteren. Door aan alle aspecten van het model aandacht te besteden bij het doorvoeren van DevOps, zal deze nieuwe werkwijze sneller en gemakkelijker in de organisatie landen. Bij het implementeren van DevOps hebben we verschillende obstakels moeten overwinnen. Daar zaten uiteraard technische problemen bij, maar we stuitten ook op organisatorische problemen die op het model zijn te projecteren. Vanuit onze praktijk geven we aan de hand van vijf vragen, inzicht in de grootste niet-technische belemmeringen die we tegen zijn gekomen en hoe we daarmee zijn omgegaan.

Hoe creëer je een gezonde omgeving waarin je met DevOps aan de slag kunt? Een gezonde omgeving voor DevOps is een omgeving waarin iedereen de voordelen draagt die DevOps te bieden heeft. Dat wil zeggen dat er wederzijds respect is en ook voor elkaars kennis en kunde. Snelle feedback wordt gewaardeerd, van zowel het systeem als van elkaar. Daarnaast is het essentieel dat er in een synergistisch proces waarde wordt opgeleverd, en dat we kunnen reageren op voortschrijdend inzicht.

Anko Tijman is Thoughtleader Agile bij Ordina en spreekt en publiceert regelmatig over Agile- onderwerpen. Hij heeft recent vanuit een beheerorganisatie meegedraaid in een Agile-team.

Frank Verbruggen is Java Software Architect en ‘DevOps-coach’ bij Ordina. Hij leidt op dit moment een ‘DevOpsteam’ bij de semioverheid.

Figuur 1: Het honingraatmodel

JAVA MAGAZINE

DevOps.indd 12

5/1/13 2:36 PM


AAN DE SLAG MET DEVOPS

Hoe creëer je zo’n cultuur? Door er zelf mee te beginnen. Door aan andere mensen te laten zien dat je wilt begrijpen wat zij aan het doen zijn. Begin zelf met het delen van kennis door bijvoorbeeld naast iemand te gaan zitten als hij zijn werk doet. Neem de tijd om de ander te begrijpen, en te horen wat er voor hem belangrijk is. Denk hierbij juist ook aan de mensen die niet in je team zitten. Hiermee laat je zien dat er wederzijds respect is en ook voor elkaars kennis en kunde. Een tweede stap is jezelf verantwoordelijk maken voor de kwaliteit van je werk. Deze moet van dusdanig hoog niveau zijn dat iemand die niet jouw vakkennis heeft met gemak kan begrijpen wat je doet. Las bijvoorbeeld een pair programmingsessie in en gebruik de feedback die de ander geeft. Alles wat hij of zij niet begrijpt aan jouw code is immers directe feedback op de kwaliteit ervan. Door jezelf open te stellen voor de kritiek van anderen wordt je code beter. Als je anderen kritisch naar jouw werk laat kijken, dan laat je daarmee zien dat je openstaat voor snelle feedback. Op basis van het nieuwe vertrouwen kun je effectief samenwerken over bestaande disciplines heen. Gebruik de synergie tussen de mensen om het proces te verbeteren. Identificeer de huidige belemmeringen in de diverse werkwijzen. Werk samen aan het wegnemen ervan en optimaliseer zo je werkproces. Wanneer de organisatie merkt dat er procesverbeteringen zijn, zal de rest snel volgen.

Welke businesscase bestaat er voor DevOps? Er bestaat een gezonde businesscase om met DevOps aan de slag te gaan, maar deze is niet direct duidelijk als je naar de traditionele werkvloer kijkt. Het denken in functionele teams leidt tot een focus op het uitvoeren van de eigen taak in plaats van het toevoegen van waarde voor de klant. De businesscase ligt in het creëren van synergie door middel van multidisciplinaire teams. Het succes van Scrum heeft aangetoond dat samenwerken met andere partijen loont. DevOps gaat een stap verder door development en operations in een team te verenigen. De verbintenis tussen development en operations levert zowel op korte als lange termijn de volgende voordelen op: 1. Teamspelers leveren betere kwaliteit. Mensen die samenwerken, bereiken meer en krijgen meer gedaan in kortere tijd. 2. Door het automatiseren van ons eigen werk kunnen we sneller en goedkoper le-

veren. We zijn immers automatiseerders en dat is waar we goed in zijn. 3. Een aanzienlijke kostenbesparing door het elimineren van wachttijden. Vanuit de Lean- gedachte is wachten pure verspilling. Door het introduceren van een relatie tussen development en operations kunnen we nieuwe elementen in ons softwareleveringsproces optimaliseren. Er bestaat in feite een wederzijdse afhankelijkheid. Je kunt niet meer zonder elkaar!

Hoe gaan ontwikkelaars en beheerders samenwerken? Cruciaal bij samenwerking is een gezamenlijk doel. Dus het eerste wat je doet als je gaat samenwerken is een doel stellen. Dat doel moet verbindend en herkenbaar zijn, en je als team in staat stellen om continu te verbeteren. Van daaruit komt vanzelf de behoefte om bij elkaar te gaan zitten. Je hebt elkaar immers nodig om dat doel na te streven. Om succesvol samen te werken, is wederzijds begrip van waar ons werk uit bestaat essentieel. Zie het als jouw verantwoordelijkheid om aan de ander uit te leggen waar je werk uit bestaat. Neem de tijd om naar elkaar te luisteren. Je moet weten waar je als team mee bezig bent en waarom je bepaalde taken al dan niet uitvoert. Nu we weten wat ons doel is en waar ons werk uit bestaat, richten we ons op het maximaal faciliteren van elkaar om ons doel te bereiken. En dat betekent dat je soms taken moet oppakken die niet in je traditionele takenpakket zitten. In een DevOps-team moet je dus af en toe uit je comfortzone komen. Maar doe dat in het teambelang.

OM SUCCESVOL SAMEN TE WERKEN, IS WEDERZIJDS BEGRIP VAN WAAR ONS WERK UIT BESTAAT ESSENTIEEL.

Waarom is geautomatiseerd en vaak deployen naar productie veiliger? Als je de keuze hebt tussen geautomatiseerd en vaak deployen naar productie of handmatig en weinig deployen naar productie, dan is Advertentie

Jij weet wat Java kan. Maar weet Java al wat jij kan? CGI heeft vacatures door het hele land. Kijk op www.werkenbijcgi.nl voor de verschillende Experience the commitment mogelijkheden. Lees meer op blz. 39.

JAVA MAGAZINE | 01 2013

DevOps.indd 13

5/1/13 2:36 PM


14

Figuur 2: Risico bij weinig en veel releases

de eerste optie veiliger. Handmatige releases naar productie zijn vaak risicovolle ondernemingen. Er zijn allerlei dingen die fout kunnen gaan en vaak gaat er ook iets mis. De menselijke factor speelt bij het maken van fouten een significante rol. In cruciale omgevingen wordt deze factor verwijzingen dan ook ingeschat en geanalyseerd. DevOps stelt om deze reden dat het een goed idee is om geautomatiseerd te deployen. Bij een geautomatiseerde deployment zorgt de programmatuur ervoor dat de deployment keer op keer op dezelfde wijze wordt uitgevoerd. Dit haalt menselijke fouten weg bij de uiteindelijke release naar productie. Daarnaast is het bij geautomatiseerde releases naar verschillende omgevingen triviaal om exact dezelfde stappen uit te voeren. Daardoor kun je gemakkelijker nieuwe omgevingen inrichten en opschalen. Wanneer we al onze deployments geautomatiseerd doen, ontstaat de neiging om vaker releases te willen doen. Het is in een geautomatiseerde omgeving gemakkelijk om een deployment te doen, omdat je deze alleen maar hoeft te starten. Als we kleinere changes releasen, is de kans minder groot dat een change een groot risico met zich mee brengt (Figuur 2). Geautomatiseerd en vaak deployen naar productie is dus veiliger. Toch moeten we als professionals rekening houden met het feit dat mensen van nature weerstand hebben tegen verandering. Gebruik die weerstand als informatie over hoe zij de verandering beschouwen. Hieronder twee voorbeelden van weerstand die we in de praktijk zijn tegengekomen en hoe we daarmee zijn omgegaan:

Er is niemand die kan besluiten of een change wel of niet door mag. < Probeer samen te achterhalen op basis waarvan de besluitnemer dit besluit zou nemen en automatiseer dat beslissingsproces. Zorg ervoor dat het beslissingsproces actueel blijft en wordt bijgehouden. De besluitnemer kan in zijn nieuwe rol het beslissingsproces steeds beter maken. Wat als er een fout in de code zit en die wordt uitgerold in productie? < Code zit altijd vol met fouten. De vraag is of wij (of de klant) die fouten detecteren. Als we fouten in het normale proces met een test hebben opgespoord, dan voegen we die test toe aan onze testsuite en vinden wij ze ook. < Programmeerfouten worden continue in productie gezet. Het is één van de grote redenen dat we beheer nodig hebben. Het loont dan ook de moeite om jezelf af te vragen, of je liever drie maanden of één uur wilt wachten tot de fout is gefikst.

PROBEER ERVOOR TE ZORGEN DAT DE ANDER DE VOORDELEN VAN DE VERANDERING INZIET, MAAR DAT JIJ OOK BEGRIJPT WAT HEM ERVAN WEERHOUDT JOUW ENTHOUSIASME TE DELEN.

Realiseer jezelf dat je andere mensen nooit kunt dwingen iets te doen. Wanneer zij weerstand hebben, dan wacht er een taak op je. Neem deze taak serieus en probeer hen te begeleiden in het veranderingsproces. Probeer ervoor te zorgen dat de ander de voordelen van de verandering inziet, maar dat jij ook begrijpt wat hem ervan weerhoudt jouw enthousiasme te delen. Onthoud wat de voordelen zijn van geautomatiseerd en vaak deployen naar productie. Deployments worden voortaan zonder menselijke fouten uitgevoerd en door kleinere changes zijn ze minder risicovol.

JAVA MAGAZINE

DevOps.indd 14

5/1/13 2:37 PM


AAN DE SLAG MET DEVOPS

Hoe kun je met DevOps laten zien dat je succesvol bent? Succes is een woord dat voor ieder mens een andere betekenis heeft. Met succes bedoelen we hier: het door het team vaststellen van doelen en ze gezamenlijk bereiken. Een doel is hier een meetbare verbetering met betrekking tot de manier waarop de software draait in productie. Om goed doelen te kunnen stellen, hebben we eerst informatie nodig. We moeten weten wat er gebeurt in de productieomgeving. Om informatie te verkrijgen, moet het team gemakkelijk data kunnen terugkrijgen van de productiemachine over wat er daadwerkelijk gebeurt. Zorg er daarom voor dat je zoveel mogelijk productiestatistieken gaat bijhouden. Gebruik hiervoor een oplossing waarmee je gemakkelijk een nieuwe statistiek kunt bijhouden. Handige tools hierbij zijn bijvoorbeeld StatsD en Graphite. Een dashboard is een visuele weergave van alle statistieken die je bijhoudt in de productieomgeving. Iedereen mag het dashboard zien en ieder teamlid kan een voorstel doen om er nog een statistiek aan toe te voegen. Door een goede duidelijke weergave van je dashboard, is het voor iedereen visueel inzichtelijk of je doelstellingen daadwerkelijk zijn behaald. Het dashboard vergroot het vertrouwen in de werking van de applicatie en maakt direct inzichtelijk als er iets aan de hand is in productie. Zo kun je problemen zo snel mogelijk detecteren en oplossen. Denk daarbij niet alleen aan technische aspecten, maar ook aan bijvoorbeeld het aantal misclicks of end-ofsession-handelingen door gebruikers. Het dashboard heb je nodig voor het stellen van doelen en om inzichtelijk te maken dat je ze behaalt. Zorg daarom voor een snelle implementatie van het dashboard bij het DevOps- implementatietraject. Definieer doelen aan de hand van het dashboard en laat aan iedereen zien dat je doelen bereikt worden. Dan ziet iedereen dat je succesvol bent.

Conclusie DevOps is een logisch vervolg op het Agilegedachtegoed. Het helpt organisaties om sneller, beter en gecontroleerd software te releasen. Het is essentieel dat je collega’s de DevOps-gedachte ondersteunen. Door mensen te verbinden, een gezamenlijk doel te stellen en processen af te stemmen op de samenwerking, ontstaat er vanzelf een sfeer, waarin je als team maximaal gebruikmaakt

van elkaars kennis en kunde. Wij hebben dit zelf succesvol gedaan en gunnen jou dit ook. Wij hopen dat we een tipje van de sluier hebben opgelicht en dat ook jij je organisatie wilt gaan verbeteren met DevOps.

VERWIJZINGEN

1. http://agilemanifesto.org/principles.html 2. Wilson, J.R. (1993). SHEAN (Simplified Human Error Analysis code) and automated THERP. United States Department of Energy Technical Report Number WINCO--11908. 3. http://dhemery.com/articles/resistance_as_a_resource/

Advertentie

Ben jij ook een Champions League speler

?

Kijk voor onze vacatures op pagina 20/21 van dit magazine.

JAVA MAGAZINE | 01 2013

DevOps.indd 15

5/1/13 2:37 PM


16

Effectieve EAI met Apache Camel Veranderende requirements en integratie behoeften Requirements in IT-projecten zijn gevoelig voor verandering, en dat geldt ook voor requirements aan de integratie met andere systemen. In staat zijn om snel te reageren op deze veranderingen kan cruciaal zijn voor een succesvol project. Gelukkig biedt Enterprise Application Integration (EAI) ons alle kennis, technologie en best practices om uitbreidbare, onderhoudbare integratie oplossingen te bouwen op een productieve manier.

De meeste integratie oplossingen stellen ons echter wel voor een dilemma: terwijl ze vol met mogelijkheden zitten en heel geschikt zijn voor complexe projecten en een veeleisende omgeving, vereisen zij ook grote investeringen vooraf als het gaat om het leren van het systeem en het onderhouden ervan. Om deze reden lijken ad-hoc oplossingen erg aantrekkelijk op het moment dat onze integratie eisen simpel zijn. Maar ze worden moeilijk te onderhouden en contraproductief indien de integratie behoeften groeien. Het toepassen van EAI best practices zou ons in staat kunnen stellen om een ad-hoc oplossing gaandeweg te laten groeien, maar dit vergt op zichzelf ook inspanning en kennis. Hoe kunnen we dan productief zijn wanneer wij worden geconfronteerd met zowel eenvoudige als complexe integratie problemen, terwijl we grote investeringen vooraf vermijden? In dit artikel zal ik betogen dat Apache Camel een oplossing biedt. Ik zal laten zien dat Camel kan voldoen aan complexe integratie vraagstukken maar ook gemakkelijk is op te pakken en gemakkelijk te beheersen.

public class FTPFetch { public static void main(String[] args) { FTPClient ftp = new FTPClient(); try { ftp.connect(“host”); //probeer te verbinden if (!ftp.login(“camel”, “apache”)){ //log in op de server ftp.disconnect(); return; } int reply = ftp.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { ftp.logout(); ftp.disconnect(); return; } ftp.changeWorkingDirectory(“folder”);

xml”);

ftp.retrieveFile(“file.xml”, output); //doe de data

overdracht

output.close(); ftp.logout(); ftp.disconnect();

} catch (Exception ex) { ex.printStackTrace();

Bescheiden begin Integratie begint vaak simpel. Zoals het ophalen van een bestand vanaf een FTP server om het lokaal op te slaan. In dit stadium lijkt de doe-het-zelf oplossing erg aantrekkelijk. Maar laten we dat eens nader bekijken. Deze oplossing zou er als volgt uit kunnen zien: Deze oplossing maakt gebruik van de FTPClient klasse van Apache Commons. Omdat

//maak een stream aan naar het bestemmingsbestand file.xml OutputStream output = new FileOutputStream(“data/outbox/file.

}

} finally { if (ftp.isConnected()) { try { ftp.disconnect(); } catch (IOException ioException) { ioException.printStackTrace(); } } }

JAVA MAGAZINE

EAI met Apache Camel.indd 16

5/1/13 2:43 PM


EAI MET APACHE CAMEL

het maar een client is en niets meer, moeten we het opzetten van een FTP verbinding en foutafhandeling zelf regelen. Maar wat als het bestand op de FTP server later verandert? Om dergelijke veranderingen te kunnen detecteren zouden we dan zelf code moeten schrijven om bijvoorbeeld dit bestand periodiek binnen te halen. Laten we nu eens kijken naar Apache Camel. Camel is een framework voor applicatie integratie en ontworpen om dit soort problemen op te lossen door het volgen van EAI best practices. Camel kan worden gezien als een gereedschapskist van kant en klare integratie componenten. Maar het is ook een runtime die voor specifieke behoeften kan worden aangepast door deze componenten aan te vullen en te combineren. Dit is hoe we het probleem hierboven zouden oplossen met Camel:

public class CamelRunner{ public static void main(String args[]) throws Exception { Main camelMain = new Main(); camelMain.enableHangupSupport(); //ctrl-c shutdown camelMain.addRouteBuilder(new RouteBuilder() { public void configure() { from( “ftp://host/folder?username=cam el&password=apache&fileName=file. xml&delay=360000” ) .to(“file:data/ outbox”); } }); camelMain.run(); //Camel blijft onbeperkt draaien } }

Let op de from en to methoden. Camel noemt dit een ‘route’: het pad dat wordt doorlopen door de gegevens vanaf de bron naar de bestemming. Bericht bronnen en bestemmingen worden ‘endpoints’ genoemd, en het is door hen dat Camel data ontvangt en verzendt. Endpoints worden opgegeven met een URI geformatteerde string zoals te zien in de argumenten voor de from en to methoden. Op deze manier worden de routes declaratief aangemaakt en geregistreerd bij Camel, die deze informatie gebruikt om runtime te weten wat het moet doen. De rest is enkel boilerplate code die wordt hergebruikt als meer routes worden toegevoegd en is veel eenvoudiger dan rechtstreeks babbelen met een FTP server. Camel zal ook

zorg dragen voor de lastige FTP details en zal zelfs het periodiek benaderen van de server voor zijn rekening nemen voor het geval het bestand verandert, aangezien we Camel hebben geconfigureerd om voor onbepaalde tijd te blijven draaien in de laatste regel. De from en to methoden zijn onderdeel van de Camel DSL, een Domain Specific Language waarvan het ‘domein’ EAI is. Dat betekent dat in tegenstelling tot andere oplossingen er geen vertaling hoeft te worden gemaakt tussen de EAI taal en de Camel taal: beide zijn vrijwel gelijk. Dit helpt om de leercurve te beperken en het instappunt laag te houden. Dus als je eenmaal je probleem in EAI termen hebt doorgrond, is het een relatief kleine stap om het uit te voeren met Camel. Het resultaat is compacte en effectieve code. Maar de code die je schrijft is niet het enige dat compact is: alles wat nodig is om dit draaiende te krijgen is camel-core. jar en camel-ftp.jar (plus afhankelijkheden), samen slechts een paar MB. De main klasse CamelRunner kan dan worden uitgevoerd vanaf de commandline. Dus hoewel je Camel prima kunt uitrollen in een volwaardig J2EE application server zoals JBoss, word je er niet toe gedwongen. Je doet het dus wanneer het jou uitkomt, niet omdat het van je EAI vendor moet. Dus het kiezen van een doe-het-zelfoplossing om de enkele reden dat frameworks veel complexiteit toevoegen is niet geldig: Camel is eenvoudig te begrijpen, eenvoudig te gebruiken en eenvoudig uit te rollen.

‘Frans van der Lek is een software engineer bij Capgemini met ervaring in web, mobile en EAI oplossingen. Als hij niet bezig is software te maken of te bedenken brengt hij graag zijn tijd door met zijn gezin, een goed boek of een lekkere kop koffie.’

Vertrouwen op Camel We hebben net kunnen zien dat Camel weinig reden overlaat om op een doe-het-zelf manier integratie problemen aan te pakken. Om dit punt te maken is echter een erg simpel probleem behandeld. De vraag is dan wel of Camel nog steeds een geschikte keuze is voor complexere situaties. Moeten we dan alsnog een van de zwaardere concurrenten kiezen? Dat is mijns inziens niet nodig. Met zijn uitgebreide ondersteuning voor allerlei transport mechanismen zoals JMS, HTTP, SMTP, en nog veel meer is het mogelijk om met een veelheid aan systemen te koppelen. Integratie vraagstukken met complexe logica zijn met de Camel DSL op doeltreffende wijze op te lossen, omdat de DSL nauw aansluit op het integratie domein. Camel kent ook voorzieningen voor zelfgemaakte uitbreidingen, zodat je als ontwikkelaar de zaak in eigen hand kan nemen mochten de standaard Camel bouwstenen niet toereikend zijn. Bijzonder aan Camel is ook de ondersteuning voor het maken van unittests, tegenwoordig een onmisbaar wapen

JAVA MAGAZINE | 01 2013

EAI met Apache Camel.indd 17

5/1/13 2:43 PM


18

in het arsenaal van iedere ontwikkelaar. De vorige paragraaf heeft een aantal manieren geschetst waarmee Camel ons helpt complexiteit van welk omvang ook het hoofd te bieden. Een EAI systeem kan echter voor uitdagingen van een compleet andere aard komen te staan, en ook aan die uitdagingen willen we kunnen voldoen. Om die te begrijpen moeten we echter een stapje terug doen zodat we het geheel waarin Camel opereert in ogenschouw kunnen nemen. Een belangrijk kenmerk van integratie oplossingen is het feit dat ze een tussenstation vormen tussen systemen. Daarmee is het echter zelf een single point of failure. En naarmate meer en meer systemen onderling verbonden worden door middel van het centrale EAI systeem zullen storingen, gegevensverlies en performance daling steeds minder aanvaardbaar worden (voor zover ze het al waren). En dat terwijl het volume aan data alleen maar toeneemt. Camel kan niet op zichzelf dergelijke eisen volledig adresseren, daar is het simpelweg niet voor gemaakt. Camel is echter wel een centraal onderdeel in een dergelijke oplossing, want het bevat alle logica voor het verplaatsen van gegevens en het verbinden van alle andere systemen. Het is dus belangrijk om te weten dat Camel zijn taken kan blijven vervullen, ook in deze veeleisende omstandigheden. Laten we een concreet voorbeeld bekijken om te zien hoe aan zulke eisen doorgaans wordt voldaan. In dit voorbeeld is er een inkomende JMS queue waar berichten worden geplaatst door externe systemen. Camels taak zal zijn om de berichten op te pakken, te verwerken, en ze vervolgens te plaatsen in een uitgaande JMS queue. Laten we er vanuit gaan dat de verwerking vrij complex en rekenintensief zou kunnen zijn, maar voor de rest geheel op de node zelf plaatsvindt. JMS queues kennen hun eigen mechanismen om persistent te worden gemaakt en ‘high available’, wat betekent dat ze een hoge mate van beschikbaarheid hebben. Met dit gegeven gaan we ervan uit dat externe systemen ‘altijd’ berichten op de inkomende queue kunnen plaatsen. Totdat het vol raakt natuurlijk, hetgeen zal gebeuren indien Camel de verwerking niet snel genoeg kan uitvoeren. Ons doel is dan om Camel bestand te maken tegen systeem storingen zodat het ook high available wordt, en de prestaties te verhogen. Dat doen we door het inzetten van meerdere servers (nodes), die elk een eigen Camel instantie runnen die aangesloten is op dezelfde eindpunten. Zie ook de afbeelding hiernaast. Dit heeft twee consequenties: ten eerste wor-

den de berichten uit de queue door meerdere Camel instanties opgepakt en parallel verwerkt, zodat het systeem sneller wordt. Een bericht dat door een instantie wordt verwerkt is niet langer beschikbaar voor de anderen. Berichten worden dus slechts eenmaal verwerkt. Ten tweede wordt het werk van een eventueel uitgevallen node automatisch overgenomen door de andere, nog draaiende nodes. De werklast wordt bovendien verdeeld over de nodes die berichten oppakken: snellere nodes kunnen berichten in een hoger tempo verwerken en nemen daarmee vanzelf meer werk voor hun rekening dan langzame nodes. Op deze manier wordt de coördinatie bereikt die nodig is om de verwerking van alle berichten correct en efficiënt te laten verlopen. Er is echter een element dat ontbreekt: mocht er namelijk een node uitvallen tijdens het verwerken van een bericht, dan moet een andere die verwerking overnemen want anders gaat dat bericht verloren. En mochten alle nodes uitvallen, dan moeten berichten die op dat moment in verwerking zijn ook niet verloren gaan. Om dat te kunnen bereiken maken we gebruik van transacties. Met transacties zal de JMS queue wachten op een bevestiging van de node die het bericht opnam alvorens het echt weg te gooien. Mocht de node uitvallen tijdens de verwerking dan komt die bevestiging nooit, en uiteindelijk zal er een rollback van de transactie plaatsvinden waardoor het bericht opnieuw terugkomt in de queue en weer beschikbaar zal zijn voor de nodes die nog wel actief zijn. Als er geen nodes actief zijn, blijft het bericht gewoon daar tot er een node uiteindelijk weer in de lucht komt. Voor Camel betekent dit dat de routes transactioneel moeten worden gemaakt zodat ze mee kunnen doen in een transactie. Camel heeft op zichzelf geen

IK RAAD IEDEREEN AAN DIE EEN EFFECTIEVE EN FLEXIBELE INTEGRATIE OPLOSSING ZOEKT OM ZICH WAT VERDER IN CAMEL TE VERDIEPEN.

JAVA MAGAZINE

EAI met Apache Camel.indd 18

5/1/13 2:43 PM


EAI MET APACHE CAMEL

<beans //namespace declaraties verwijderd > //connectie naar jms server opzetten <jee:jndi-lookup id=”jmsConnectionFactory” jndi-name=”ConnectionFactory”> <jee:environment> java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory java.naming.factory.url.pkgs=org.jboss.naming.client java.naming.provider.url=jnp://localhost:1099 </jee:environment> </jee:jndi-lookup> //configuratie voor de transactionele jms client <bean id=”jmsConfig” class=”org.apache.camel.component.jms.JmsConfiguration”> <property name=”connectionFactory” ref=”jmsConnectionFactory”/> <property name=”transactionManager” ref=”jmsTransactionManager”/> <property name=”transacted” value=”true”/> <property name=”acknowledgementModeName” value=”TRANSACTED”/> <property name=”cacheLevelName” value=”CACHE_NONE”/> <property name=”transactionTimeout” value=”5”/> </bean> //registreer camel jms component bean <bean id=”jboss” class=”org.apache.camel.component.jms.JmsComponent”> <property name=”configuration” ref=”jmsConfig” /> </bean> //registreer spring transactionmanager bean <bean id=”jmsTransactionManager” class=”org.springframework.jms.connection.JmsTransactionManager”> <property name=”connectionFactory” ref=”jmsConnectionFactory”/> </bean> <camelContext xmlns=”http://camel.apache.org/schema/spring”> <route> <from uri=”jboss:queue:incoming”/> <transacted/> <log loggingLevel=”INFO” message=”processing started.” /> <!— tijdrovende verwerking hier --> <to uri=”jboss:queue:outgoing?exchangePattern=InOnly” /> </route> </camelContext> </beans>

ondersteuning voor transacties, maar in plaats daarvan maakt het gebruik van 3rd party oplossingen. Dat houdt Camel eenvoudig, terwijl gebruik kan worden gemaakt van bewezen technologieën terwijl het wisselen van implementaties wordt vergemakkelijkt. Als voorbeeld zullen we nu Camel met transacties configureren binnen een Spring container, zodat we de Spring JmsTransactionManager kunnen gebruiken. Merk op dat aangezien we binnen Spring draaien, het praktischer is om de Spring XML versie van de Camel DSL te gebruiken in plaats van de Java versie, ook al is dat laatste is ideaal voor beginners. De Camel DSL bestaat in meerdere varianten, niet alleen Spring/Java maar bijvoorbeeld ook Scala of Groovy. Nou betekent het veranderen van DSL’s tijdens een project uiteraard rework, dus is het belangrijk om te migreren op een geschikt moment. Gelukkig draait de Spring DSL ook in een unittest, zodat

unittests kunnen helpen om veilig de overgang te maken want ze zullen werken op routes ongeacht welk DSL type werd gebruikt. Met de <transacted/> tag wordt de route gemarkeerd als transactioneel, zodat Camel alle stappen van die route binnen een transactie zal uitvoeren. Dat zorgt ervoor dat in het geval van een storing tijdens de verwerking van deze route er een rollback van de transactie plaatsvindt en dat het bericht dus terug komt in de inkomende berichten queue.

Conclusie Apache Camel heeft een gemakkelijke leercurve en is licht in gebruik en uitrol zodat investeringen vooraf klein zijn. Zelfs in vrij eenvoudige gevallen kan het gebruiken van Camel een snellere weg naar integratie zijn dan doe-het-zelf oplossingen. Camel biedt dus zonder meer laagdrempelige toegang tot de best practices van EAI.

Maar Camel is ook prima inzetbaar in meer veeleisende omgevingen, bijvoorbeeld als onderdeel van een oplossing met hoge eisen ten aanzien van beschikbaarheid. Over het geheel genomen is Camel een uitstekende optie voor integratie van vrijwel elke omvang en complexiteit: je kunt klein en eenvoudig beginnen met een minimale investering vooraf in de wetenschap dat mochten integratie behoeften complexer worden, Camel je niet teleur zal stellen. In de tussentijd kun je productief blijven, terwijl je de vruchten plukt van een volwaardig integratie framework. In dit artikel hebben we maar een paar kanten van Camel kunnen belichten, er is nog veel meer dat Camel bijzonder en de moeite waard maakt zoals de DSL en unittest support. Ik raad dan ook iedereen aan die een effectieve en flexibele integratie oplossing zoekt om zich wat verder in Camel te verdiepen.

JAVA MAGAZINE | 01 2013

EAI met Apache Camel.indd 19

5/1/13 2:44 PM


Ben jij ook een

Champions League s

Bekijk dan de volgende vacatures • • • • •

Junior en Senior Java Software Developer Java Integratie Specialist Java Solution Architect Open Source Developer Open Source Architect

03-021.13a_Advertisement_JavaMagazine_420x275_v1_220413.indd Untitled-1 2 1

29-4-2013 10:09:36


?

e speler

Meer informatie kijk op www.werkenbijcapgemini.nl of neem contact op met: Kees Heerschap Recruitment Manager Tel. 31 6 4609 3926 I E-mail: kees.heerschap@capgemini.com

Untitled-1 3

4/24/2013 29-4-201312:36:55 10:09:36PM


22

MongoDB, de database van de toekomst? De evolutie van databases Big Data is al lang geen hype meer. Data groeit niet alleen wat betreft hoeveelheid, ook de structuur van opslaan verandert. Omdat de hoeveelheid data toeneemt, wordt cloud computing steeds vaker ingezet. Daarnaast moet data vanuit elke business geanalyseerd worden en het liefst realtime beschikbaar zijn. Bijvoorbeeld op dashboards op je eigen device (BYOD) zodat beleid eventueel snel kan worden aangepast. We kennen allemaal het aloude relationele model, dat door veel productleveranciers is geĂŻmplementeerd in een Relationeel DataBase Management System (RDBMS). Denk bijvoorbeeld aan de relationele databases van Oracle, Microsoft en IBM. Rond het jaar 2000 was er veel behoefte aan salesreporting, marketinginformatie etc. en waren de zogenaamde On Line Analytical Processing (OLAP) datawarehouses populair. Data werd op een andere manier gemodelleerd (stermodellen met dimensions en facts) en het redundant opslaan van data was toegestaan. Vanaf 2010 zijn NoSQLdatabases steeds populairder geworden, wat niet betekent dat relationele databases en datawarehouses gaan verdwijnen. Ze zullen naast elkaar blijven bestaan. NoSQL database implementaties maken onder andere gebruik van andere vormen van opslag dan het klassieke relationele model. NoSQL databases zijn er in diverse verschijningen, en in veel gevallen doen ze iets wat met relationele databases minder goed kan. Een van de meest populaire databases van de laatste tijd is MongoDB. We zullen in dit artikel verder op deze database ingaan. Wat maakt MongoDB interessant en wat betekent deze ontwikkeling voor de Java ontwikkelaar?

MongoDB en 10gen MongoDB is een open source document oriented database geschreven in C++. De data is opgeslagen als binairy JSON ook bekend als BSON. De eerste release is uitgebracht in 2009 en momenteel heeft v2.4 de productionele status. De database kan gemakkelijk gedistribueerd worden. De data wordt

dan over meerdere computers verspreid om gedistribueerde gegevensverwerking mogelijk te maken. Er is geen ondersteuning voor joins en ondersteuning voor transacties is beperkt. MongoDB is beter geschikt voor Big Data dan een relationele database van Oracle, MySQL of Microsoft. Enkele belangrijke productionele omgevingen zijn Foursquare, bit.ly en CERN voor het verzamelen van grote hoeveelheden data. De belangrijkste features op een rij: < Document data model with

dynamic schemas < Full, flexible index support and

Maikel Alderhout is Solution Architect bij VX Company en organizer van de Dutch MongoDB User Group (www.dutchmug.org)

rich queries < Auto-Sharding for horizontal

scalability < Built-in replication for high < < < <

availability Text search Advanced security Aggregation Framework and MapReduce Large media storage with GridFS

10gen is het bedrijf achter MongoDB en begeleidt de MongoDB ontwikkeling, ondersteunt de grote en groeiende MongoDB community, biedt commerciĂŤle abonnementen inclusief ondersteuning, advies en training. Daarnaast biedt 10gen het gratis, cloud-gebaseerde MongoDB Monitoring Service (MMS).

Installeren van MongoDB Je kan MongoDB installeren op verschillende platformen, op zowel 32 bits als 64 bits architecturen. Zie voor meer informatie op http:// docs.mongodb.org/manual/installation/

JAVA MAGAZINE

MongoDB.indd 22

5/1/13 2:45 PM


MONGODB

CRUD CV Database We maken een simple CV database aan, waar we personen en expertisegroepen via CRUD functionaliteit kunnen beheren. Log aan in de MongoDB shell met “./mongo” > use cv switched to db cv

Insert Op het moment van een insert, is de database CV ook echt aangemaakt: > db.person.insert( { “name” : “Bas”, “group” : [ “Open Source”, “mongoDB”, “Big Data” ], } )

en nog 1 toevoegen: > db.person.insert( { “name” : “Maikel”, “group” : [ “Oracle”, “ExaData”, “Big Data”], } )

In MongoDB wordt “person” een collectie genoemd, en de rijen worden documents genoemd. We kunnen nu het aantal documents tellen: > db.person.count() 2

Retrieve Met behulp van het find commando kunnen we het document opvragen: > db.person.find() { “_id” : ObjectId(“5145adfcd6edf3d8f2d 67108”), “name” : “Bas”, “group” : [ “Open Source”, “mongoDB”, “Big Data” ] } { “_id” : ObjectId(“5145ae14d6edf3d8f2d 67109”), “name” : “Maikel”, “group” : [ “Oracle”, “ExaData”, “Big Data” ] }

Wat nu opvalt is het veld “_id” van het type ObjectId. Een Objectid is een 12-byte BSON type, deze fungeert als primaire sleutel en is opgebouwd uit: een 4-byte tijdstempel, een 3-byte machine-id, een 2-byte proces-ID en een 3-byte teller, die gevuld wordt met een willekeurige waarde. We kunnen nu ook documenten selectief selecteren. Geef alle documenten met een groep “Oracle”.

> db.person.find({“group” : “Oracle” }) { “_id” : ObjectId(“5145ae14d6edf3d8f2d 67109”), “name” : “Maikel”, “group” : [ “Oracle”, “ExaData”, “Big Data” ] }

Update Stel we besluiten het veld “group” te verwijderen bij het document met de naam “Maikel” > db.person.update({name: “Maikel”}, {$unset: {group: 1}}) > db.person.find() { “_id” : ObjectId(“5145adfcd6edf3d8f2d 67108”), “name” : “Bas”, “group” : [ “Open Source”, “mongoDB”, “Big Data” ] } { “_id” : ObjectId(“5145ae14d6edf3d8f2d 67109”), “name” : “Maikel” }

Of we vervangen alle group items bij het document met name “Bas” > db.person.update({name: “Bas” },{ $set: {“group” : [“EOSS”, “SQL”, “OpenStack”]}}) > db.person.find() { “_id” :

Of we voegen een group item “Oracle” toe, bij het document met de naam “Bas” > db.person.update({name: “Bas” }, { $push: { group: “Oracle” } }) > db.person.find() { “_id” : ObjectId(“5145ae14d6edf3d8f2d6 7109”), “name” : “Maikel” } { “_id” : ObjectId(“5145adfcd6edf3d8f2d6 7108”), “group” : [ “EOSS”, “SQL”, “OpenStack”, “Oracle” ], “name” : “Bas” }

Upsert Een upsert is een speciaal type update. Als er geen document wordt gevonden dat aan de update criteria voldoet, zal een nieuw document worden gecreëerd. Als er een overeenkomend document wordt gevonden, zal het op de normale manier worden ge-update. Voor het gebruik van Upsert voeg je de parameter “true” toe. > db.person.update({name: “Jan” },{ $set: {“group” : [“OpenStack”]}},true) > db.person.find() { “_id” : ObjectId(“5145ae14d 6edf3d8f2d67109”), “name” : “Maikel” } { “_id” : ObjectId(“5145adfcd6ed f3d8f2d67108”), “group” : [ “EOSS”, “SQL”, “OpenStack”, “Oracle” ], “name” : “Bas” } { “_id” : ObjectId(“5145c1fbcb 8282695dbab2af”), “group” : [ “OpenStack” ], “name” : “Jan” }

JAVA MAGAZINE | 01 2013

MongoDB.indd 23

5/1/13 2:45 PM


24

Multiple update Ook hier geldt het toevoegen van de parameter “true”. > db.person.update( { }, { $set: {“group” : [“EOSS”, “mongoDB”, “OpenStack”]}}, true, true ) > db.person.find() { “_id” : ObjectId(“5145ae14d6edf3d8f2d 67109”), “group” : [ “EOSS”, “mongoDB”, “OpenStack” ], “name” : “Maikel” } { “_id” : ObjectId(“5145adfcd6edf3d8f2d 67108”), “group” : [ “EOSS”, “mongoDB”, “OpenStack” ], “name” : “Bas” } { “_id” : ObjectId(“5145c1fbcb8282 695dbab2af”), “group” : [ “EOSS”, “mongoDB”, “OpenStack” ], “name” : “Jan” }

Delete Stel we willen het document met de naam “Maikel” verwijderen: > db.person.remove({“name”: “Maikel”}) > db.person.find() { “_id” : ObjectId(“5145adfcd6edf3d8f2d 67108”), “group” : [ “EOSS”, “mongoDB”, “OpenStack” ], “name” : “Bas” } { “_id” : ObjectId(“5145c1fbcb8282695db ab2af”), “group” : [ “EOSS”, “mongoDB”, “OpenStack” ], “name” : “Jan” }

Stel we willen de hele collectie “person” verwijderen:

Log in op de primary instance om de replica set yet te configuren. Het eerste wat je ziet na het rs.initiate() commando is een foutmelding. RS staat voor replica set. mongo localhost:40001 MongoDB shell version: 2.2.3 connecting to: localhost:40001/test > rs.initiate() { “info2” : “no configuration explicitly specified -- making one”, “me” : “Computername.local:40001”, “info” : “Config now saved locally. Should come online in about a minute.”, “ok” : 1 }

Start nu de configuratie door de secundary en de arbiter te koppelen. person:PRIMARY> rs.add(Computername:40002) { “ok” : 1 } person:PRIMARY> rs.add(Computername:40003, {arbiterOnly:true}) { “ok” : 1 }

Kijk nu met behulp van het rs.status() commando. De primary, secundary en de arbiter zijn opgezet:

> db.person.drop()

Replicatie Replicatie is zeer eenvoudig op te zetten met MongoDB. We gaan een primary (node1) en secundary (node2) instance aanmaken, met een aparte arbiter instance. Read/Write

App

Reads (Optional) Reads (Optional)

Primary

Secundary

Asynchronous Replication

Secundary

Een arbiter bepaalt de nieuwe primary in geval deze er niet meer is. Start met aanmaken van de data directories: mkdir /data/node1 mkdir /data/node2 mkdir /data/arbiter

Start nu elke instance op in een aparte terminalsessie: mongod --port mongod --port mongod --port

--replSet person --dbpath /data/node1 40001 --replSet person --dbpath /data/node2 40002 --replSet person --dbpath /data/arbiter 40003

person:PRIMARY> rs.status() { “set” : “person”, “date” : ISODate(“2012-10-28T19:50:52Z”), “myState” : 1, “members” : [ { “_id” : 0, “name” : “Computername.local:40001”, “health” : 1, “state” : 1, “stateStr” : “PRIMARY”, “uptime” : 1266, “optime” : Timestamp(1351453811000, 1), “optimeDate” : ISODate(“2012-10-28T19:50:11Z”), “self” : true }, { “_id” : 1, “name” : “Computername.local:40002”, “health” : 1, “state” : 2, “stateStr” : “SECONDARY”, “uptime” : 41, “optime” : Timestamp(1351453811000, 1), “optimeDate” : ISODate(“2012-10-28T19:50:11Z”), “lastHeartbeat” : ISODate(“2012-1028T19:50:51Z”), “pingMs” : 0v } ], “ok” : 1 } { “_id” : 1, “name” : “Computername.local:40003”, “health” : 1,

JAVA MAGAZINE

MongoDB.indd 24

5/1/13 2:45 PM


MONGODB

Vervolg code: “state” : 3, “stateStr” : “ARBITER”, “uptime” : 14, “optime” : Timestamp(1351453811000, 1), “optimeDate” : ISODate(“2012-10-28T19:50:11Z”), “lastHeartbeat” : ISODate(“2012-1028T19:50:51Z”), “pingMs” : 0 } ], “ok” : 1 }

Het is nu tijd om een test te doen door middel van het toevoegen van een document in de primary instance: person:PRIMARY> use cv switched to db cv person:PRIMARY> db.person.save( { “name” : “Maikel”, “group” : [ “Oracle”, “ExaData”, “Big Data”], } )

Log in op de secundary en gebruik nog wel het rs.slaveOk() command om aan te geven dat lezen mogelijk is. Test of de collection en het document ook hier bestaan: mongo localhost:40002 MongoDB shell version: 2.2.3 connecting to: localhost:40002/test person:SECONDARY> rs.slaveOk() person:SECONDARY> use cv switched to db cv person:SECONDARY> db.person.find() { “_id” : ObjectId(“508d971dda0730903]bcbb612”), “name” : “Maikel”, “group” : [ “Oracle”, “ExaData”, “Big Data” ] } person:SECONDARY>

We gaan nu een scriptje maken dat 1000000 documents (persons) genereert: person:PRIMARY> for(i=0; i<1000000; i++) { db.person.save({person: i}); }

Log aan op de secundary en check of de data ook hier binnenkomt: person:SECONDARY> db.person.find() { “_id” : ObjectId(“508f95e9e38917f 43ae20db3”), “person” : 0 } { “_id” : ObjectId(“508f95e9e38917f 43ae20db4”), “person” : 1 } { “_id” : ObjectId(“508f95e9e38917f 43ae20db5”), “person” : 2 } { “_id” : ObjectId(“508f95e9e38917f 43ae20db6”), “person” : 3 } { “_id” : ObjectId(“508f95e9e38917f 43ae20db7”), “person” : 4 } { “_id” : ObjectId(“508f95e9e38917f 43ae20db8”), “person” : 5 } { “_id” : ObjectId(“508f95e9e38917f

Vervolg code: 43ae20db9”), “person” : 6 } { “_id” : ObjectId(“508f95e9e38917f 43ae20dba”), “person” : 7 } { “_id” : ObjectId(“508f95e9e38917f 43ae20dbb”), “person” : 8 } { “_id” : ObjectId(“508f95e9e38917f 43ae20dbc”), “person” : 9 } { “_id” : ObjectId(“508f95e9e38917f 43ae20dbd”), “person” : 10 } { “_id” : ObjectId(“508f95e9e38917f 43ae20dbe”), “person” : 11 } { “_id” : ObjectId(“508f95e9e38917f 43ae20dbf”), “person” : 12 } { “_id” : ObjectId(“508f95e9e38917f 43ae20dc0”), “person” : 13 } { “_id” : ObjectId(“508f95e9e38917f 43ae20dc1”), “person” : 14 } { “_id” : ObjectId(“508f95e9e38917f 43ae20dc2”), “person” : 15 } { “_id” : ObjectId(“508f95e9e38917f 43ae20dc3”), “person” : 16 } { “_id” : ObjectId(“508f95e9e38917f 43ae20dc4”), “person” : 17 } { “_id” : ObjectId(“508f95e9e38917f 43ae20dc5”), “person” : 18 } { “_id” : ObjectId(“508f95e9e38917f 43ae20dc6”), “person” : 19 } Type “it” for more person:SECONDARY> db.person.count() 194079 person:SECONDARY> db.person.count() 215657 person:SECONDARY> db.person.count() 228488 person:SECONDARY> db.person.count() 239528 person:SECONDARY>

Het werkt! Download nu de nieuwste Java driver voor MongoDB van: https://github.com/mongodb/mongo-java-driver/ downloads Maak een nieuw Java project aan in Eclipse of een andere IDE. Voeg de gedownloade JAR file toe in het build path. Gebruik de code op de volgende pagina om je main class op te zetten.

10gen is met MongoDB community gedreven, iedereen kan suggesties (tickets) inbrengen en volgen op: https://jira.mongodb.org/ Voordelen MongoDB < MongoDB kan als generieke database worden ingezet en is flexibel in te richten < MongoDB kan overweg met grote hoeveelheden data: • hoog beschikbaar onder andere door middel van replicatie • horizontale schaalbaarheid onder andere door middel van sharding < Flexibel modelleren (geen joins, maar nested values) < Gemakkelijk in gebruik

JAVA MAGAZINE | 01 2013

MongoDB.indd 25

5/1/13 2:45 PM


26

package nl.nljug; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import com.mongodb.*; /** * Simple Person Report finding persons in a particular group. * */ public class PersonReport { /** * Find a persons which participating in a special interest group. * @param groupName * @param personCollection * @return cursor to persons found */ public DBCursor getPersonWithGroup(String groupName, DBCollection personCollection){ // Construct JSON with DBObjects DBObject fields = new BasicDBObject(“group”, groupName); // Show what we are doing... System.out.println(fields); // Sort on name (descent order) DBObject orderBy = new BasicDBObject(“name”, -1); // return persons which met the conditions return personCollection.find(fields).sort(orderBy); } public static void main(String[] args) throws UnknownHostException{ // Application is aware of replicaset of mongoInstances List addr = new ArrayList(); addr.add(new ServerAddress(“127.0.0.1”, 40001)); addr.add(new ServerAddress(“127.0.0.1”, 40002)); addr.add(new ServerAddress(“127.0.0.1”, 40003)); // Connect to replica set Mongo mongo = new Mongo(addr); // Get the Person Collections from test database DBCollection collection = mongo.getDB(“test”).getCollection(“person”); // Just for the fun count number of documents in person collection System.out.println(“ number of documents : “ + collection.getCount()); PersonReport personReport = new PersonReport(); DBCursor cursor = personReport.getPersonWithGroup(“Big Data”, collection); // Loop through found persons... try { while(cursor.hasNext()) { System.out.println(cursor.next()); } } finally { cursor.close(); } } }

Na het starten krijg je de volgende output: number of documents : 1000001 { “group” : “Big Data”} { “_id” : { “$oid” : “50904c1912644ddba9ec6c1e”} , “name” : “Maikel” , “group” : [ “Oracle” , “ExaData” , “Big Data”]}

Nu is het tijd om de primary instance te stoppen (Figuur 1). Check wat er gebeurt met de secondary (rs.status()). De arbiter kiest een nieuwe primary instance. Start het Java programma opnieuw. Als het goed is krijg je gewoon dezelfde output, zonder het Java programma

aan te passen (Figuur 2)! Als de oude primary weer in de lucht komt, start het recovery proces. De oude primary fungeert nu als nieuwe secondary. Ook nu zal de applicatie gewoon blijven werken!

MongoDB v2.4 features Op het moment van schrijven heeft v2.4 de productie status gekregen. Hieronder de belangrijkste features: Text search is een van de meest gevraagde functies voor MongoDB en 10gen werkt al een tijdje aan een experimentele tekst-zoekfunctie. Advies is wel om text search niet direct op de productieomgevingen te

Primary

App

Secundary

Secundary

Primary

App

Automatic election of new Primary

Secundary

Secundary

Figuur 1

Recovering

App

Primary

Secundary

Secundary

App

Primary

Secundary

Figuur 2

implementeren, maar eerst te proberen op testomgevingen. Google’s V8 zal Mozilla’s SpiderMonkey vervangen als de onderliggende JavaScript-engine. Hoewel er wellicht enkele voordelen behaald kunnen worden, kan er geen sprake zijn van opmerkelijke verschillen voor de meeste MongoDB gebruikers. Er zijn wel benchmarks uitgevoerd, waaruit blijkt dat het effect het grootste is in MongoDB omgevingen waar sharding (partitioning) is ingezet. Verder zijn er nog enkele nieuwe (Geospatial) indexen bijgekomen en is hash-based sharding geïntroduceerd, om nog betrouwbaarder de data te distribueren over de verschillende shards. Daarnaast is er nu een modulair opgezet authenticatie mechanisme met support voor Kerberos.

JAVA MAGAZINE

MongoDB.indd 26

5/1/13 2:45 PM


LIBRARIES UITGELICHT

Libraries uitgelicht JSefa

Log4J 2.0

Je wilt even een csv bestand inlezen en mappen naar een entiteit voor verdere verwerking. Daar zijn genoeg libraries voor, waarvan openCSV wel de bekendste is. Wat JSefa bijzonder maakt, is dat het een generieke java serializer voor XML, CSV en FLR op basis van JSR 173(Stax) is. Daarnaast kun je JSefa ook uitbreiden om eigen formaten te ondersteunen. Het mooie is dat je door middel van annotaties aangeeft, hoe en op welke velden de mapping moet plaatsvinden en dit voor de verschillende formaten. Ideaal om bijvoorbeeld een JPA entiteit te decoreren met csv en/of xml annotaties . Daarna is serialisatie een kwestie van factory oproepen met een reader of writer en de geannoteerde klasse en gaan.

Log4J is, ondanks dat het een echte klassieker is, waarschijnlijk de meest populaire library ooit. Duizenden applicaties gebruiken al vele jaren Log4J in het hart van hun systemen om kritische meldingen te loggen. Zal wel goed werken toch? Echte Log4J veteranen weten wel beter. Memory leaks, subtiele performance problemen, mysterieuze reacties als niet kan worden geschreven, een vaak verwarrende configuratie die runtime niet foutloos te wijzigen is. Log4J 2.0 (http://logging.apache.org/log4j/2.x/) heeft als doel om verbeteringen te adopteren uit het alternatieve framework Logback. Een moderne, prettige API, gestroomlijnde configuratie die runtime gewijzigd kan worden zonder logging events te verliezen, hoge performance en robuust gedrag bij schrijfproblemen. Momenteel nog in beta, maar zeker een aanrader om te volgen.

De Java API Het is voor sommigen een tweede natuur. Je hebt een bepaalde functionaliteit nodig voor je Java oplossing en via je zoekmachine vind je al snel een nieuwe library die je toevoegt aan je project. Veelal zit die functionaliteit in de standaard API van Java. Het is zeker waar dat sommige oplossingen omslachtige regels code vereisen en niet altijd evident zijn. Het is echter de vraag of dit de introductie van een nieuwe library rechtvaardigt of dat een eigen geschreven utility class voldoet. De kunst is deze verborgen juweeltjes in de Java API te vinden en handig toe te passen in jouw oplossing. Misschien is dit toch een overweging voordat je op zoek gaat naar de zoveelste handige library van de benodigde functionaliteit.

JavaParser Wellicht kan iedereen zich de volgende situatie wel voor de geest halen, je moet een refactoring doorvoeren, maar je favoriete IDE biedt deze mogelijk niet of levert een hele berg aan compilatie fouten op, welke vervolgens handmatig moeten worden aangepast. Met behulp van JavaParser is het mogelijk om je eigen refactoring tools te schrijven, deze library zorgt voor het inlezen van de source code en vervolgens kun je over de AST lopen en deze aanpassen en vervolgens weer wegschrijven. In een aantal gevallen loont het zeker om op deze manier slim een refactoring uit te voeren, maar er zijn natuurlijk nog tal van andere toepassingen te bedenken voor deze kleine maar krachtige library. Voor meer informatie zie http://code.google.com/p/javaparser

XMLUnit Als je veel met webservices werkt, komt het geregeld voor dat je voor testcases stukken XML met elkaar wil vergelijken. Op het eerste oog lijkt dat een triviale klus, totdat je er ermee aan de slag gaat: je krijgt te maken met namespaces, al dan niet self closing elementen, niet exact te voorspellen waarden (zoals timestamps), noem maar op. XMLUnit biedt hierbij hulp met twee uitbreidingen op JUnit: XMLAssert en XMLTestCase. Hiermee kun je assertions maken voor: < overeenkomsten of verschillen tussen twee stukken XML; < het evalueren van XPath-expressies op een stuk XML; < het valideren van XML; < het resultaat van een XSL-transformatie van een stuk XML. Als kers op de taart kun je XMLUnit naast XML ook HTML laten verwerken, zodat je deze assertions ook kunt gebruiken bij tests van webpagina’s.

JAVA MAGAZINE | 01 2013

Libraries uitgelicht.indd 27

5/1/13 2:47 PM


28

Mobiele enterprise-server Ja-Pi is de eerste Marco Hebing, Remko de Jong, en Eelco Meuter van Ordina zijn gek van de Raspberry Pi en wilden wel eens kijken wat er mogelijk is. Zo zochten ze uit hoe je met Java enkele led’s via een website kunt aansturen. Daarnaast hebben ze een eigen mobiele Java enterpriseserver gebouwd. De Raspberry Pi is een computer ter grootte van een creditcard. Het is een vernuftig apparaatje met een robuust karakter en handige aansluitingen, dat tegen een schappelijke prijs online te bestellen is, waardoor het een uitstekend platform is om mee te experimenteren. De Raspberry Pi is ontwikkeld, nadat werd geconstateerd dat de huidige generatie startende IT-studenten zelden met computers experimenteert. Vaak komt dit omdat de ouders het niet zien zitten dat zoon- of dochterlief uit nieuwsgierigheid hun kostbare bezit uit elkaar schroeft. Sinds de introductie zijn het niet alleen ouders, kinderen en onderwijsinstellingen die een Raspberry Pi aanschaffen, maar vindt het computertje ook gretig aftrek bij hobbyisten. Dit leidt tot veel grappige, handige en creatieve toepassingen met een Raspberry Pi, met name op het gebied van home automation. In dit artikel bespreken we twee toepassingen van een Raspberry Pi met Java. In de eerste toepassing sturen we een aantal led’s aan via een website. Dit project staat model voor een softwarematige aansturing van hardware via

Foto 1: Het led-printje met de koperen kant boven

de pinnen van de General Purpose Input Output (GPIO) met Java; oftewel de ‘hello world’ van de elektronica. Het tweede project is wat extremer: bouw je eigen mobiele Java enterpriseserver. In dit project installeren we een volwassen applicatieserver op de Raspberry Pi en monteren deze op een robot. Het praktisch nut van deze mobiele applicatieserver is ons nog niet helemaal duidelijk, maar dat mag de pret niet drukken. De Raspberry Pi die we tijdens de projecten gebruiken, is van het type Model B (versie 2). Er zijn twee types op de markt. Het verschil tussen Model A en B is dat Model B een extra usb- en ethernetaansluiting heeft en 512 MB RAM, in plaats van de 256 MB die in een Model A zit. Verder draait onze Raspberry Pi met de standaard Linux-distributie en is tijdens de initiële configuratie overgeklokt naar 800 Mhz.

Marco Hebing Java Software Architect is sinds anderhalf jaar werkzaam bij Ordina.

Remko de Jong werkzaam bij Ordina als senior software developer.

Led there be Pi We beginnen met het aansturen van enkele led’s via de GPIO-aansluiting (P1). Het boodschappenlijstje in het kader vermeldt de onderdelen die je nodig hebt. De GPIO-connector van de Raspberry Pi heeft 26 pinnen. Deze zijn verdeeld in 17 echte input/outputpinnen, een I2C-interface, een SPI- interface, Serial Tx- en Rx-pinnen. Als je goed naar de Raspberry Pi kijkt, dan zie je dat er een rechtstreekse verbinding is tussen de processor en de GPIO-pinnen. Alles wordt dus direct vanuit de processor aangestuurd. Kijken we naar het schema (figuur 1), dan zien we dat er per led (D1 t/m D5) ook een

Eelco Meuter Java Software Architect bij Ordina.

JAVA MAGAZINE

Raspberry.indd 28

5/1/13 2:52 PM


MOBIELE ENTERPRISE-SERVER

weerstand (R1 t/m R5) in serie is geschakeld om de stroom door elke led te beperken. Dit voorkomt beschadigingen aan de Raspberry Pi en de led’s. Een led werkt prima met 10mA en heeft daarbij zelf maar 0,7V nodig. De GPIO-pinnen leveren 3,3V, waardoor er dus nog 2,6V overblijft die we met een weerstand veilig wegwerken. De weerstandswaarde kunnen we berekenen met de formule R(Ω) = U(V) / I(A). In dit geval 2,6V / 0,01A = 260Ω. Die weerstandswaarde is geen standaardwaarde, dus ronden we af naar boven en komen we uit op 270Ω. We hebben dit ledprintje op de eenvoudigste manier gemaakt. Daarbij komt de print, met de koperen kant boven, op de Raspberry Pi te liggen. De connector zit nu aan de onderkant. De foto laat zien wat de bedoeling is. Alle andere componenten solderen we op de koperzijde, zodat ze niet de onderdelen van de Raspberry Pi kunnen raken. Vanaf P1-pin 7 (figuur 1) solderen we eerst de weerstand R1. Vanaf de andere kant van de weerstand gaan we door naar de lange aansluiting van led D1. De korte aansluiting van de led verbinden we door met pin 6 van P1, dit is de min, ofwel ground. Herhaal dit voor alle weerstanden en led’s in het schema. Om de GPIO-pinnen vanuit de processor aan te sturen, gebruiken we WiringPi, een library geschreven in C door Gordon Drogon. Let op! WiringPi gebruikt een eigen nummering voor de pinnen. Deze wijkt af van de GPIO- nummers zoals benoemd in figuur 1. Om vanuit Java met WiringPi te kunnen praten, gebruiken we Pi4J. Met Pi4J kunnen we de benodigde pinnen definiëren als output. Immers, wij schakelen de led’s aan of uit. Het definiëren van een pin kunnen we maar één keer doen. Het is dus belangrijk om na de initialisatie de referentie naar de pin vast te houden. Dit kan heel simpel door gebruik te maken van een singleton die de objectreferenties naar de pinnen beheert. Nu moeten we alleen nog een client bouwen om de singleton aan te roepen. Wij kozen voor een simpele JSP, maar je kunt natuurlijk ook je eigen front-end bouwen in bijvoorbeeld JavaFX. Vanuit het aansturen van led’s via een JSP is het natuurlijk nog maar een kleine stap naar een mobiele enterprise-applicatie.

Pi op rupsbanden, uitgerust met een op een servo gemonteerde webcam, wifi, infraroodsensoren en een paar rijregelaars en schakelaars. De beelden van de webcam worden realtime verwerkt via Motion, een Linux-tool voor live video capture. Links op figuur 2 zie je duidelijk de GPIO-connector. Rechts naast de GPIO bevinden zich de comparatoren voor de infraroodsensoren. Bovenaan het schema bevinden zich de connectoren voor de rijregelaars en servo. De besturing van de robot is beperkt tot het

Figuur 1: Schema van een Raspberry Pi Connector met ons ledproject P1

GPIO

Led

WiringPi

Pin 7

4

D1

7

Bouw je eigen mobiele Ja-Pi enterprise-server

Pin 11

17

D2

0

Pin 15

22

D3

3

In dit volgende project richten we onze Raspberry Pi in als een enterprise-server en monteren we hem op een robot. De opbouw van het voertuig is niets meer dan een Raspberry

Pin 21

9

D4

13

Pin 26

7

D5

11

Tabel 1: Mapping van de de Raspberry Pi naar WiringPi

JAVA MAGAZINE

Raspberry.indd 29

5/1/13 2:52 PM


30

Figuur 2: Schema voor de bouw van de robot

versnellen en vertragen van de snelheid en het draaien van de robot en camera. Vooruitrijden betekent dat we de snelheid van beide rupsbanden tegelijkertijd veranderen. We kunnen een bocht maken door de snelheid van de ene rupsband te verhogen ten opzichte van de andere band. Het aanpassen van de snelheid van de elektromotoren gebeurt via de rijregelaars. De opzet van de applicatie is vrij simpel. Er is een eenvoudige HTML5-pagina, waarmee we via AJAX-requests besturingscommando’s uitvoeren. Een aantal controllers vangt deze REST-calls op. We maken hiervoor gebruik van JAX-RS. In deze controllers injecteren we onze Robot-class. Deze class bevat de logica om de robot te besturen en bevat een referentie van de UnitManager. De UnitManagerclass stuurt commando’s uit naar de driver die de robot en camera besturen. Daarnaast leest de UnitManager de sensoren uit via WiringPi. Voor de applicatieserver kozen we in eerste instantie voor de IBM Websphere. Volgens de brochure is het nieuwe Liberty Profile geheel configureerbaar en gebruik je enkel de features die je nodig hebt. De eerste testen waren veelbelovend. Websphere Librerty Profile draaide al snel en gemakkelijk op onze Raspberry Pi. Na de toevoeging van CDI, een singleton bean, enkele asynchrone beans en een timerservice in onze applicatie, ging het echter mis. Dus exit IBM, welkom JBoss. Tijd voor de volgende uitdaging. Om de motoren en servo aan te sturen, schrijven we een commando vanuit onze applicatie naar ServoBlaster; een driver om servo’s en motoren

softwarematig aan te sturen. Dit moet multithreaded gebeuren, want de twee rupsbanden moeten simultaan draaien. Je eigen threads beheren op een applicatieserver is niet aan te raden. Om dit te simuleren, maken we gebruik van een timerservice. Dit werkt als volgt. Stel, we willen de snelheid van de robot opvoeren. We creëren dan twee getimede events met een kleine time-out voor respectievelijk de linker- en rechterrupsband. Op de time-out van dit event voeren we het commando uit in een aparte thread. Het bovenstaand codevoorbeeld toont ook nog een RCUnit-class. Deze class is een datacontainer voor onder andere de nieuwe positie

HET PRAKTISCH NUT VAN DEZE MOBIELE APPLICATIESERVER IS ONS NOG NIET HELEMAAL DUIDELIJK, MAAR DAT MAG DE PRET NIET DRUKKEN.

HTML5

JSON REST API UnitManager

WiringPi

Command

Servoblaster

Figuur 3: Applicatiearchitectuur voor de aansturing van de robot

JAVA MAGAZINE

Raspberry.indd 30

5/1/13 2:54 PM


MOBIELE ENTERPRISE-SERVER

SHOPPINGLIST

@Singleton @Startup public class UnitManager {

1 Raspberry Pi, Model B (versie 2)

@Inject private UnitCommand command;

Project 1 < 5 led’s, maakt niet uit welke kleur. < 5 weerstanden 270ℌ, 0,25W < 26-polige (2x13) female printheader < Stuk experimenteerprint 4x5cm (waarbij drie gaatjes steeds verbonden zijn) < Lichte soldeerbout, ongeveer 15W < Soldeertin

@Resource private TimerService timerService; @Timeout public void executeCommand(Timer timer) { command.execute((RCUnit) timer.getInfo());

} /**

* Creates a new command. * * @param timer interval at which command is executed * @param info Required data to be passed to command */ public void createCommand( int timer, RCUnit info ) { timerService.createSingleActionTimer( timer, new TimerConfig(info, false ); } }

// rest of class

@Stateless public class UnitCommand { @Asynchronous public void execute(RCUnit unit) { writeCommandToServoBlaster(unit); } }

// rest of class

REFERENTIES

Raspberry Pi: http://raspberrypi.org Pin layout P1: http://elinux.org/Rpi_Low-level_peripherals WiringPi: http://projects.drogon.net/raspberry-pi/wiringpi Motion: http://www.lavrsen.dk/foswiki/bin/view/Motion/WebHome ServoBlaster: https://github.com/richardghirst/PiBits/tree/master/ServoBlaster Pi4J: http://pi4j.com/ YouTube: http://www.youtube.com/user/RaspberryPiOrdina

en de grenswaarden van de betreffende servo of motor. Nadat het in elkaar zetten van de robot en het testen van de software, was het tijd om alle Linux-libraries, de applicatieserver en onze webapplicatie te installeren. Dit is erg makkelijk en na een paar minuten opstarttijd heb je beeld. Je kunt nu een mobiele applicatieserver aansturen vanaf een website. Als bestuurder word je netjes gewaarschuwd voor een naderend object. Een eventuele botsing zorgt ervoor dat de motoren en servo geen stroom meer krijgen. Een simpele refresh van de webpagina herinitialiseert de robot en je kunt weer op weg. Mocht je nieuwsgierig zijn naar het eindresultaat, bekijk dan het YouTube-filmpje.

Wij hopen dat deze projecten je inspireren en zijn erg benieuwd met welk (wild) idee jij binnenkort aan de slag gaat. Laat het weten aan de redactie van het Java Magazine. Wat het ook zal zijn, je zal er een hoop van leren en veel lol aan beleven. Veel plezier!

Project 2 < Onderstel Robot < 2 rijregelaars < 6V accupack + acculader < Stuk experimenteerprint < 4 micoswitches voor de bumpers < 1 standaard servo < 2 IDC-connectoren voor de rijregelaar < 2 infrarood afstandsensoren < 1 power switch < 1 diode 1N4004 < 1 condensator 4700uF/10V < 1 flatcable 2x13 polig < 1 male printheader 2x13 polig voor flatcable < 3 male printheader 1x3 polig voor servo en rijregelaars < 1 IC LM2902N < 4x weerstand 220k < 2x weerstand 470k < 2x weerstand 1M < 1 WI-PI, wifi dongle < 1 webcam < 1 sinaasappelkistje < Verf en wat plamuur

Advertentie

Ben jij ook een Champions League speler

?

Kijk voor onze vacatures op pagina 20/21 van dit magazine.

JAVA MAGAZINE

Raspberry.indd 31

5/1/13 2:54 PM


32

Grafische User Interface testen met Java en Sikuli

In ons dagelijkse werk wordt er veelvuldig gebruik gemaakt van automatische testen met als doel sneller opleveren met een vastgestelde kwaliteit. Grafische User Interface (GUI) testen worden om verschillende redenen minder vaak ingezet. Dit artikel behandelt de positie van GUI testen in het landschap van mogelijke automatische testvormen. Concreet worden ervaringen en best practices gedeeld over “Sikuli”, een populaire en flexibele open source GUI testtool.

Sikuli is een open source grafische scherm analyse tool en is ontwikkeld binnen het Massachusetts Institute of Technology (MIT). Sikuli werkt op basis van images en pixel vergelijking. Hierdoor is Sikuli implementatie onafhankelijk en kunnen er zowel web- als native clients mee getest worden. De Java API van Sikuli is gebruiksvriendelijk, doordat het een klein aantal simpele en intuïtieve methodes bevat. Door deze Java code te combineren met het page object pattern, kunnen er onderhoudbare en overzichtelijke GUItesten worden gemaakt. Daarnaast maakt de integratie met JUnit het mogelijk om deze testen toe te voegen aan een (bestaande) automatische build. Dit artikel geeft inzicht in het gebruik van Sikuli en de Java API en biedt handvatten om de testen onderhoudbaar op te zetten.

Positionering GUI testen Om GUI-testen beter te kunnen positioneren wordt er gekeken naar de test automation pyramid . De test automation pyramid (Figuur 1) geeft een indicatie van de verhouding testen per testsoort, dat wil zeggen unit-, integratie of systeem en acceptatie testen. De pyramid geeft geen indicatie wat er getest wordt. Unittesten kunnen bijvoorbeeld ook worden gebruikt om onderdelen van de UI te testen. In de pyramid staat de unit test laag voor een solide basis. Wellicht door externe wijzigingen valt er een test om, maar daarmee nog

niet de hele testset. Unittesten zijn fijnmazig en testen slechts kleine delen van de applicatie onafhankelijk van elkaar. Wijzigingen aan het systeem raken slechts een klein deel van de unittesten die eventueel onderhoud behoeven. De tweede laag is de service laag, ook wel de integratielaag genoemd. De service of integratie testen in deze laag controleren of de verschillende componenten of services van een applicatie correct met elkaar integreren. De scope van een integratie test is dus aanzienlijk groter, waardoor wijzigingen aan de applicatie ook sneller kunnen leiden tot het plegen van onderhoud aan de testen. De top bevat de UI testen. Hiervoor geldt het worst case scenario dat aanpassingen aan het systeem ertoe kunnen leiden dat alle testen in deze laag onderhoud behoeven. De UI of ook wel systeem testen in deze laag testen volledige flows, waarbij een enkele test een groot gedeelte van de applicatie code raakt. Bij het bepalen van het type test dat men schrijft, kan de stelregel gehanteerd worden dat een test op een zo laag mogelijk niveau wordt geplaatst. Logica in een methode dient getest te worden op unit test niveau. Testen op integratie of GUI testniveau raken Figuur 1 De Test Automation Pyramid deze logica uiter-

Hubert ten Hove Werkzaam bij Info Support als Ontwikkelaar / Ontwerper

JAVA MAGAZINE

Sikuli.indd 32

5/1/13 2:56 PM


GUI TESTEN MET JAVA EN SIKULI

aard wel indirect, maar deze typen testen mogen niet als doel het testen van die methode hebben. Lager in de piramide geplaatste testen zijn niet alleen goedkoper om te maken, maar geven ook sneller nuttige feedback. Des te hoger een test in de piramide, des te meer setup hij behoeft en hoe complexer de test vaak is. Daarnaast kunnen hoger geplaatstse testen pas geschreven worden als er grote delen van de applicatie klaar zijn. Een GUI test wordt niet voor niets vaak een systeem test genoemd. Dit geeft aan hoeveel functionaliteit een applicatie al moet bezitten voor de GUI test gemaakt kan worden. Voor het maken van deze GUI-testen wordt er gebruik gemaakt van Sikuli.

Sikuli Sikuli werkt op basis van pixel vergelijking. Het zoekt naar images op het scherm en vergelijkt deze met opgeslagen images. Het vergelijken van de images gaat op basis van een threshold, welke default op 80% is ingesteld. Dit betekent dat een image, op pixel niveau, voor 80% overeen moet komen. Naast de threshold kunnen er verschillende instellingen gedaan worden zoals zoeken op geschaalde of gedraaide images. Daarnaast kan Sikuli ook tekst op het scherm herkennen. Door puur grafisch te werken op basis van pixel vergelijking, is Sikuli totaal onafhankelijk van een framework of implementatie. Andere GUI testtools, zoals Rational Functional Tester , SWTBot of Selenium , haken direct in op specifieke GUI frameworks, en zijn daardoor complexer en sterker gekoppeld aan de gebruikte technologie. De functionaliteit van Sikuli wordt ontsloten door een Java API. De twee belangrijkste klassen in deze API zijn DesktopScreenRegion, DesktopMouse en ImageTarget. De DesktopScreenRegion wordt gebruikt om het scherm te benaderen, en biedt methodes om images op het scherm te vinden en om delen van het scherm te benaderen. De DesktopMouse klasse wordt gebruikt om muisklikken te kunnen simuleren op het scherm. Sikuli werkt op basis van images, de ImageTarget klasse is de Sikuli implementatie van een image. De basis van Sikuli werkt erg simpel,

zie het code voorbeeld in Figuur 3. Maak een instantie van DesktopScreenRegion en een ImageTarget aan. Door de find methode wordt het image op het scherm gevonden. De methode retourneert een ScreenRegion waar het gevonden image zich bevindt, en null als het niet gevonden wordt. Via de DekstopMouse klasse kan er geklikt worden op coördinaten in het scherm. Deze krijgt als waarde mee het centrum van de gevonden ScreenRegion. De ImageTarget klasse kan aangemaakt worden door middel van een File maar ook door een URL. Verder bied de klasse de mogelijkheid om de evaluatie criteria in te stellen. Denk daarbij aan criteria als het percentage wat overeen moet komen (threshold). Het code voorbeeld in Figuur 4 toont het gebruik van ImageTarget waarbij de setMinScore methode het threshold instelt op 95%.

Het Page Object Pattern GUI-testen vergen vaak veel setup, dit maakt de setup code lang, onleesbaar en niet onderhoudbaar. Het pageobject pattern biedt een oplossing voor deze problematiek. De pageobjecten modeleren een applicatie aan de hand van zijn schermen en dialogen. Pageobjecten zijn in code geschreven representaties van grafische schermen of delen van deze schermen. Een grafisch scherm bevat een aantal knoppen, of andere interactie punten of toont een presentatie van data. Het pageobject modelleert elk scherm aan de hand van zijn interacties. Elke interactie van een scherm wordt een methode van het pageobject. Als een interactie op een scherm resulteert in het openen van een scherm of dialoog, zal pageobject dit ook implementeren. De methode van het pageobject zal

een pageobject van het nieuwe scherm terug geven. De unittesten benaderen de applicatie door middel van zijn pageobjecten. Het onderstaande sequence diagram (Figuur 5) toont hoe vanuit een unittest de applicatie gestart en ingesteld kan worden. De starter klasse zorgt voor het opstarten van de applicatie. Na het starten van de applicatie geeft de klasse een pageobject terug van het login dialoog. Het pageobject geeft toegang tot het logindialoog. Het dialoog pageobject biedt de mogelijkheid om in te loggen. Deze geeft dan een pageobject van het applicatie hoofdscherm terug. De unittest opent vervolgens een dialoog om instellingen aan de applicatie te doen. De implementatie van een pageobject wordt toegelicht aan de hand van een voorbeeld. Er wordt een pageobject gecreëerd voor een opslaan dialoog (Figuur 6). Het dialoog kent een viertal acties: < Opslaan < Annuleren < Browse < Bestandsnaam opgeven Het pageobject voor dit dialoog zal deze acties als methodes implementeren. De code voor het basis pageobject ziet er uit als het codevoorbeeld in Figuur 7. De constructor zoekt het dialoog op basis van een image. Het gevonden image wordt opgeslagen als de Region waarbinnen de verdere zoekopdrachten gedaan worden. Dit versnelt het zoekproces aanzienlijk omdat je nu alleen binnen een bepaalde region verder hoeft te zoeken en niet het hele scherm. De klasse wordt daarna uitgebreid met de acties van het dialoog. Opslaan en annuleren zijn eenvoudig te implementeren, door een clickmethode

ScreenRegion screen = newDesktop ScreenRegion(); ImageTarget button = new ImageTarget(new File(“c:\images\buttonImage.png”)); ScreenRegion buttonRegion = screen.find(target); DesktopMouse mouse = new DesktopMouse(); mouse.click(buttonRegion.getCenter());

Figuur 3 Sikuli Code voorbeeld ImageTarget target = new ImageTarget(new File(“c:\images\buttonImage.png”)); target.setMinScore(0.95d);

Figuur 4 Pattern Code voorbeeld

JAVA MAGAZINE | 01 2013

Sikuli.indd 33

5/1/13 2:56 PM


34

Figuur 6 Opslaan dialoog public class SaveDialoog{ private ScreenRegion dialoog; public SaveDialoog(){ ScreenRegion screen = new DesktopScreenRegion(); ImageTarget dialoogImage = new ImageTarget (“C:\images\ VolledigeDialoog.png”); dialoog = screen. find(dialoogImage); } }

Figuur 5 Sequence Diagram

aan te roepen via een image region van de button. Het gebruik van de region helpt hierbij, hierdoor is er de zekerheid dat er nergens anders op het scherm een zelfde button staat. De browse button opent een nieuw dialoog. Het aanroepen van deze methode returned dan ook een pageobject van dit nieuwe dialoog. Naast het opslaan en annuleren kan er een browse dialoog geopend worden. De browse methode geeft dan een pageobject terug voor het browse dialoog dat geopend zal worden op het scherm. De laatste actie is het opgeven van een bestandsnaam. Voer een klik uit in het centrum van het image uit Figuur 9. Hierdoor zal de cursor in het bestandsnaam veld komen te staan. Daarna kan er via de DesktopKeyboard klasse tekst in geplaatst worden.

JUnit Door de pageobjecten te gebruiken wordt een setup van een unittest overzichtelijk en leesbaar. De setup vergt vaak niet meer dan een aantal regels code. Het volgende code voorbeeld (Figuur 9) laat zien hoe de setup van een unittest in de applicatie er uit ziet. Dit is de implementatie van het sequence

Figuur 7 Code Voorbeeld page object

diagram uit Figuur 5. Het opstarten van de client en het inloggen is iets wat bij voorkeur gedaan wordt in de setup van de klasse en niet voor elke unittest opnieuw. Dit kan door een setup methode met de @BeforeClass tag op te nemen. Het afsluiten gebeurt dan in een teardown methode met een @AfterClass tag. De client moet voor elke unittest terug gebracht worden naar een bepaalde uitgangspositie. Een falende unittest heeft hierdoor geen invloed op de opvolgende testen. Dit kan zelfs betekenen dat het verstandig is om de gehele client af te sluiten en opnieuw te starten. Na de setup van de test moeten de controles uitgevoerd worden. Sikuli assert de client aan de hand van images. Deze images representeren de client met de juiste schermen en met de juiste gegevens op het scherm. Hiervoor wordt gebruik gemaakt van de find methode in Sikuli in combinatie met de default threshold van 80% of door deze in te stellen (Figuur 10).

Uitdagingen Bij het maken van de testen zijn er de volgende uitdagingen: < GUI-testen zijn breekbaar en vergen in vergelijking tot integratie of unit

testen veel onderhoud. < Het zoeken van images op het

scherm is langzaam; < GUI-testen moeten een zinnige

foutmelding geven; < Instellen van de threshold voor

een image; Onderhoudbaarheid GUI-testen Het pageobject pattern scheidt de setup test code van de daadwerkelijke testen, wat hergebruik over verschillende testen bevordert. Door gebruik te maken van het pattern worden de testen leesbaarder en beter onderhoudbaar. Door de applicatie op te delen in schermen hoeft men alleen het pageobject van het gewijzigde scherm aan te passen. Dit zal op de GUI testen zelf weinig tot geen effect hebben. Performance bij zoeken Pageobjecten maken gebruik van de Screen klasse om toegang te krijgen tot het scherm. Om de zoekopdrachten te versnellen wordt het scherm opgedeeld in regions. Dit opdelen kan door het opgeven van coördinaten, maar ook op basis van de zoek resultaten (Figuur 7). Niet alleen versnelt het opdelen de testen maar voorkomt het ook de kans op fouten. Vooral bij het zoeken naar images die vaker kun-

JAVA MAGAZINE

Sikuli.indd 34

5/1/13 2:56 PM


GUI TESTEN MET JAVA EN SIKULI

nen voorkomen op het scherm, denk hierbij aan een ‘Ok’ knop. Figuur 9 Sikuli zoek image

Foutmeldingen Zoals altijd met het schrijven van code is het ook voor GUI testen erg belangrijk zinnige foutmeldingen te genereren. Sikuli geeft null terug als een image niet gevonden wordt op het scherm. Er moet een keuze gemaakt worden om dit af te handelen in de unittest d.m.v. een exception of in het pageobject. De keuze valt op het direct afhandelen in het pageobject (Figuur 11). Hier kan namelijk een duidelijke foutmelding gegenereerd worden op basis van de bekende gegevens. Het pageobject handelt een niet gevonden image af door een fail aan te roepen. De fail methode faalt de unittest die gebruik maakt van het pageobject. Best practice is het maken van een screenshot om de foutmelding te ondersteunen. Er is een eigen SikuliTool klasse gemaakt welke op basis van een meegegeven Region een screenshot maakt en saved. De save methode geeft een melding terug met daarin de locatie van het screenshot en een verwijzing naar het gezochte image op het scherm. Deze visuele feedback op de falende unittesten is zeer waardevol tijdens het debuggen en om te bepalen waarom een nightly build gefaald is.

LoginDialoog loginDialoog = new MidClientOpstarter().startMidClient(); Client client = loginDialoog.login(“gb”, “gb”); InstellenInhoudDialoog dialoog = client.openInstellenInhoudDialoog(); dialoog.setTijd(Ma, 12:00, Di, 4:00); dialoog. … dialoog.ok();

Figuur 10 Setup Code van een Unittest

DesktopScreenRegion screen = new DesktopScreenRegion(); ImageTarget imageTarget = new ImageTarget(new File(“c:\images\image.png”)); imageTarget.setMinScore(0.99d); ScreenRegion exists = screen.find(imagePattern); if (exists == null) { fail(“Het image wordt niet of niet juist weer gegeven.”); }

Figuur 11 Voorbeeld “assertion” using Sikuli

public void cancel(){ ImageTarget button = new ImageTarget(new File(“c:\images\ cancelButton.png”)); ScreenRegion buttonRegion = dialoog.find(button); if(buttonRegion != null){ mouse.click(buttonRegion.getCenter()); } else { fail(“De cancel button kan niet gevonden worden.” + SikuliTool.saveScherm(dialoog, “C:\images\cancelButton.png”)); } }

Figuur 12 Fail in pageobjecten

Instellen threshold Om testen betrouwbaar te maken is het instellen van een goede threshold belangrijk. Als de threshold te licht ingesteld wordt zal Sikuli te veel herkennen. Met een te zwaar ingestelde threshold kunnen testen falen. Een threshold van 100% is niet aan te raden. Er treden in een client altijd kleine verschillen op door anti aliasing of een schaduw die net iets anders berekend is. Voor native clients kunnen verschillende Windows themes ook problemen geven. Als best practice wordt een threshold van 98% aangeraden als iets volledig goed op het scherm dient te staan. Sikuli herkent de meeste GUI items met de default instellingen.

Conclusie Ondanks dat de Sikuli tool zich nog in versie 1.0 RC3 bevindt wordt het

al twee jaar ingezet bij een klant van ons. Het heeft ons al meerdere malen nuttige feedback gegeven. De falende GUI testen zijn een indicatie van missende unittesten. Deze moeten dan ook uitgebreid worden. De roadmap voor de 1.0 release is sinds november 2012 beschikbaar evenals een standalone Java API. Het gebruik van het pageobject pattern komt de onderhoudbaarheid van de testen ten goede en wordt als zeer prettig ervaren. Een aanpassing aan een venster in de client betekent niet meer dan een aanpassing aan een pageobject, zonder de unittesten aan te hoeven passen. Mede door een goede foutmelding met het bijgeleverde screenshot waren de falende testen snel te debuggen. Het kwam niet vaak voor dat een test opnieuw gestart

moest worden en een ontwikkelaar de acties op het scherm moest volgen om de fout te ontdekken. De screenshots zijn een uitkomst. Sikuli en pageobject pattern maken het makkelijk om GUI testen te schrijven. Sikuli heeft een korte leercurve. Een ontwikkelteam heeft het binnen een paar dagen goed onder de knie. Hierdoor kan er met weinig tijd en moeite een onderhoudbare en begrijpbare set GUI testen gecreeerd worden. Dat is de grote kracht bij het gebruik van Sikuli met het pageobject pattern.

JAVA MAGAZINE | 01 2013

Sikuli.indd 35

5/1/13 2:57 PM


36

Van het bestuur Een update vanuit het NLJUG bestuur, met dit keer nieuws over Java Magazine, aandacht voor organisatieveranderingen binnen de NLJUG, een terugblik op J-Fall 2012 en een vooruitblik op de rest van het jaar.

Java Magazine We zijn terug! Na een jaar uit de lucht geweest te zijn, presenteren we als bestuur met trots het vernieuwde Java Magazine. Begin 2012 werden we overvallen door het nieuws dat de uitgever ten onder was gegaan aan een faillissement. Na een uitgebreide zoektocht naar een nieuwe mediapartner voor de NLJUG, zijn we verheugd om te melden dat we het blad nu volledig in eigen beheer maken en uitgeven. Niet in de laatste plaats dankzij de hulp van een aantal enthousiaste NLJUG leden die de redactiecommissie van het blad vormen. Wil je zelf graag bijdragen door iets te schrijven voor het magazine? Aarzel dan niet en neem contact met ons op!

Organisatieveranderingen NLJUG Ondanks dat we niet zichtbaar waren in de vorm van een magazine, zijn we in 2012 zeker niet onzichtbaar geweest. Naast een conferentie en andere events, vond een belangrijk deel van wat er het afgelopen jaar is gebeurd echter plaats achter de schermen. Een Java User Group is een complex ding. Een JUG van de grond krijgen is makkelijk. Alles wat je nodig hebt zijn een paar enthousiaste mensen en een plek om bij elkaar te komen. De eerste paar maanden of jaren kost het vrijwel geen moeite om dat kunstje te blijven herhalen. Echter, zodra

je ledenaantallen fors gaan groeien en je ‘zaken’ gaat doen met businesspartners, evenementenbureau’s en mediapartners verander je langzaam van een club enthousiastelingen bij een thema tot een bedrijf. Om die fase te overleven moet je je ook gaan gedragen als bedrijf. En dat betekent dat de spaarzame avonduren en regenachtige zondagmiddagen ineens gevuld gaan worden met saaie bezigheden als ledenadministratie, boekhouding, projectleiding en wat al niet meer. Aangezien de NLJUG voor geen van de bestuursleden een day-job is, hebben we ons de afgelopen jaren met vallen en opstaan door die dynamiek heen gewerkt. Sinds 2003 (ja, ja, 2013 is daarmee dus een bijzonder jaar!) zijn we de NLJUG in haar huidige vorm aan het rollen. Om ook de komende 10 jaar dezelfde en verbeterde continuïteit te bieden, hebben we als bestuur het besluit genomen, om de volledige operationele activiteiten van de NLJUG onder te brengen in een BV die daarbij zowel het secretariaat (leden- en financiële administratie), het magazine, de website en de evenementen gaat uitvoeren. Deze BV ‘zit er ondernemend in’ en heeft veel ervaring met het runnen van vergelijkbare communities in aanpalende vakgebieden. Maar wees gerust, de identiteit van de NLJUG zal ten alle tijden worden bewaakt door de stichting en het bestuur. We gaan de boel niet commercieel uitventen.

Java Magazine is

TERUG Bert Ertman Bestuurslid NLJUG

JAVA MAGAZINE

Bestuurscolumn.indd 36

5/1/13 2:59 PM


VAN HET BESTUUR

Vooruitblik op 2013

Mede als gevolg van de organisatieveranderingen is er na 10 jaar NLJUG ook een einde gekomen aan de betrokkenheid van een aantal oudgediende bestuursleden. Per 1 april 2013 hebben we als NLJUG bestuur afscheid genomen van Klaasjan Tukker en Hans Bossenbroek. Vanaf deze plaats willen we hen ontzettend bedanken voor de grenzeloze inzet die zij geheel belangeloos hebben getoond, om de NLJUG door weer en wind gestalte te geven en uit te bouwen. Door inzet van hen beiden is de NLJUG opgericht en uitgebouwd tot het succesverhaal wat we vandaag de dag zijn. Als dank voor hun inzet zijn ze erelid van de NLJUG geworden.

J-Fall 2012 Het is al bijna weer een half jaar geleden, maar er valt nog steeds na te genieten van J-Fall 2012. Op de website vind je een terugblik in de vorm van een video-impressie en verwijzingen naar blogs, de fotogalerij en de resultaten van de evaluaties. Ook zijn de presentaties van bijna alle sessies te downloaden. Ongeveer de helft van de presentaties van J-Fall 2012 is op video opgenomen en terug te kijken op Parleys.com. Kijk hiervoor bij ‘spaces’ in het NLJUG channel. De presentaties zijn tevens te bekijken via de speciale NLJUG iPad App die je kunt vinden in de Appstore. Clients voor andere platforms zijn in de maak.

In 2013 gaan we vooralsnog eerst maar eens verder op de bekende weg. University sessies voor kennisverbreding, een vernieuwde website, uiteraard J-Fall 2013 en natuurlijk weer een Magazine. Met de vernieuwde slagkracht die we als organisatie hebben gekregen kunnen we vervolgens onze dienstverlening naar zowel de leden als de businesspartners verder uitbouwen. Op 15 april j.l. hebben we daarvoor met onze businesspartners om de tafel gezeten. Leden met ideeën worden van harte uitgenodigd te reageren via twitter (@NLJUG), de mail (info@nljug.org) of door een van de bestuursleden te benaderen. Al met al kijken we terug op een heftige periode. Interessante veranderingen in de organisatie brengen een vernieuwd commitment en continuïteit. Afscheid nemen van mensen die het fundament onder de NLJUG zijn geweest geeft een dubbel gevoel, maar loslaten en anderen het verder laten brengen is tevens een kenmerk van groot vertrouwen in de toekomst. Future.get(); Advertentie

Ben jij ook een Champions League speler

?

Kijk voor onze vacatures op pagina 20/21 van dit magazine.

JAVA MAGAZINE | 01 2013

Bestuurscolumn.indd 37

5/1/13 3:22 PM


38

COLUMN

Meer met Maven Least Fat Executable Jar

In elke editie van het Java Magazine zal Robert Scholte een probleem voorleggen en deze oplossen met behulp van Maven, om meer inzicht te geven in Maven zelf en de vele beschikbare plugins. public static void main( String[] args ) { org.slf4j.LoggerFactory.getLogger( Eric.class ) .warn( “I’m not fat! I’m getting in shape!” ); }

Stel dat ik een class met deze ogenschijnlijk simpele methode wil uitvoeren als executable jar, dan heb ik een interessante uitdaging: hoe krijg ik de dependencies naar SLF4J op het classpath? Voor het uitvoeren van dit soort standalone applicaties zijn er een klein aantal opties. De meest klassieke oplossing is het verzamelen van alle jars (wat kan via dependency:copydependencies) en alles uitschrijven op de commandline: Door een manifest-file op te nemen in de jar .\target>java -cp awesom-0.4000-SNAPSHOT.jar; dependency\slf4j-api-1.5.6. jar;dependency\slf4j-simple-1.5.6.jar cgi.maven.nljug.Eric

kan ik dit al een stuk vereenvoudigen, want daarin kan ik zowel het classpath als de Main-class opgeven. Vandaar dat bijna alle ‘packaging’-plugins voorzien zijn van een optie <archive><manifest/></archive>, waarin je dit soort details in kwijt kunt. In dit geval gaat het om de opties <addClasspath/> en <mainClass/>. Op dit moment bestaat deze deliverable uit meerdere bestanden, maar mijn wens is om dit terug te brengen tot één bestand, oftewel een ‘fat jar’. Het opnemen van jars in een jar is weliswaar in theorie mogelijk, maar daarmee komt de dependency-jar zelf op het classpath en niet de inhoud van deze jar. De enige optie die je hebt, is door ervoor te zorgen dat alle bestanden van de verschillende jars gebundeld worden in één jar.

De eerste plugin waarmee dit mogelijk werd was de Maven Assembly Plugin, maar tegenwoordig is er een plugin die hier veel beter op aansluit, namelijk de Maven Shade Plugin. Zonder enige aanpassing aan de configuratie maakt deze plugin van je project al direct een fat-jar. Nadat ik met fat-jar heb gegenereerd heb ik weliswaar alle classes samengepakt in één jar, maar de kans is zeer groot dat slechts een klein deel van alle beschikbare bestanden gebruikt wordt. Het zou heel mooi zijn, als ik de jar kon ontdoen van onnodige ballast. Met de optie <minimizeJar> is het mogelijk alleen de gebruikte classes op te nemen in de jar . Met de volgende configuratie transformeer ik mijn project tot de meest compacte executable jar:

Robert Scholte IT Consultant voor CGI, teamlid van Apache Maven.

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.0</version> <executions> <execution> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation=”org.apache.maven.plugins.shade. resource.ManifestResourceTransformer”> <mainClass>cgi.maven.nljug.Eric</mainClass> </transformer> </transformers> <minimizeJar>true</minimizeJar> </configuration> </execution> </executions> </plugin>

Voor de minimizeJar is een kleine kanttekening: dit werkt alleen zolang er naar de classes gerefereerd wordt. Het zal niet werken als er gebruik gemaakt wordt van reflection in de code, zoals bijvoorbeeld Class.forName(“crazy.old.Driver”). Bekijk http://maven.apache.org/plugins/ maven-shade-plugin/ voor verdere details en andere mogelijkheden met de Maven Shade Plugin.

JAVA MAGAZINE

Column Maven.indd 38

5/1/13 3:01 PM


Geef richting aan je ontwikkeling! 71.000 professionals gingen je voor. Nu jij nog! Nederlands of Java? Jij spreekt het allebei. Ideeën van onze klanten vertaal jij eenvoudig in nieuwe technologie. Jij weet wat Java kan. Maar weet Java al wat jij kan?

Snel profiteren van nieuwe technische mogelijkheden. Dat willen onze klanten. Of het nu gaat om een innovatieve applicatie of verbetering van een bestaand systeem. Met Java is alles mogelijk. Omdat de techniek overal toegepast kan worden, zijn de mogelijkheden oneindig. Jij kent Java en kan onze klanten helpen om hun doel te bereiken. Heeft de klant een complexe keten van informatiesystemen? Jij bedenkt en bouwt een website waarin alles overzichtelijk samenkomt. En ook voor nieuwe technieken met Google Android kunnen we je natuurlijk inschakelen. En je draait je hand niet om voor nieuwe uitdagingen, zoals het bedenken van veilige software voor betaling aan de ‘pomp’ bij elektrisch rijden. Jouw expertise is de toekomst. We vragen veel van je. Kennis van het vak, enthousiasme en doorzettingsvermogen. CGI heeft namelijk alleen de beste mensen in dienst. Onze klanten weten dat ook. Daarom kunnen wij jou de meest interessante opdrachten aanbieden. Want met jouw ervaring kun je de meest innovatieve, technische en complexe opdrachten aan. Voor jouw inzet krijg je wat terug. We bieden je bijvoorbeeld alle ruimte om je te ontwikkelen. Start je als software engineer, dan kun je doorgroeien naar IT consultant of architect. Heb je managementambities, dan kun je ook team lead worden. We stimuleren je in jouw ontwikkeling, maar rekenen erop dat je zelf het voortouw neemt. Met ons certificeringstraject maak je jouw kennis aantoonbaar. Goed voor de klant, goed voor jou.

Kijk op www.werkenbijcgi.nl voor de JAVA-vacatures. Liever eerst informeren naar de mogelijkheden? Bel met Jaap van den Broek (HR - Team) via 088 - 5640000.

Untitled-2 1

Experience the commitment

24-4-2013 9:26:58


INNOVEER JIJ MET ONS MEE?

Bert van den Belt en Huub Jansen, directeuren Java Ordina

De terugkeer van JAVA Magazine is een heuse verrijking voor het vak, vinden we bij Ordina. Meer kennis helpt je immers om jezelf te ontwikkelen. Daarom organiseren we regelmatig workshops, trainingen en presentaties. Zo ook weer binnenkort.

13 juni: Innoveer Jij Mee-sessie. Schrijf je nu in! Kom op 13 juni naar onze Innoveer Jij Mee-sessie. Interactief, informatief en vooral: inspirerend. Een sessie over techniek, door en voor software ontwikkelaars en architecten, zoals jij. We beloven je: na afloop ben je helemaal up-tot-date met de actuele trends en ontwikkelingen rondom JAVA, het platform, de verschillende frameworks en tools. We zijn ook razend benieuwd naar jouw inbreng en jouw ideeĂŤn. Schrijf je in op werkenbijordina.nl. Zien we je dan?

ICT MAAR DAN VOOR MENSEN werkenbijordina.nl javaman_magazine_advertentie.indd 1 Untitled-1 1

26-04-13 13:24 29-4-2013 10:08:39


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.