First8 Java Consultancy https://technology.first8.nl First8 is gespecialiseerd in het pragmatisch ontwikkelen van bedrijfskritische Java toepassingen. Wed, 21 Feb 2018 21:39:16 +0000 nl hourly 1 https://wordpress.org/?v=4.8.5 Wachtwoorden: 3 mythes, 4 fouten en 5 tips voor ontwikkelaars (deel 1) https://technology.first8.nl/wachtwoorden-3-mythes-4-fouten-en-5-tips-voor-ontwikkelaars-deel-1/ Wed, 21 Feb 2018 21:39:16 +0000 https://technology.first8.nl/?p=6069 Als ontwikkelaars hebben we tegenwoordig een grote verantwoordelijkheid. We moeten ervoor zorgen dat onze gebruikers zich zo veilig en betrouwbaar mogelijk kunnen identificeren. In deel één van deze blog vertelde ik over de mythes, fouten en tips voor gebruikers. In dit deel zal ik toelichten waar wij als ontwikkelaars vaak de fout in gaan. In deel 3 zal ik de … Lees verder Wachtwoorden: 3 mythes, 4 fouten en 5 tips voor ontwikkelaars (deel 1)

Het bericht Wachtwoorden: 3 mythes, 4 fouten en 5 tips voor ontwikkelaars (deel 1) verscheen eerst op First8 Java Consultancy.

]]>
Als ontwikkelaars hebben we tegenwoordig een grote verantwoordelijkheid. We moeten ervoor zorgen dat onze gebruikers zich zo veilig en betrouwbaar mogelijk kunnen identificeren. In deel één van deze blog vertelde ik over de mythes, fouten en tips voor gebruikers. In dit deel zal ik toelichten waar wij als ontwikkelaars vaak de fout in gaan. In deel 3 zal ik de tips gericht op ontwikkelaars beschrijven.

Als Senior Java Developer bij First8 houd ik me tijdens en buiten werktijden bezig met security en privacy. Door kennis hierover voor mezelf en mijn vakgenoten te vergroten, hoop ik de wereld veiliger te maken.

Ook in dit deel zal ik een aantal mythes ontkrachten. Daarna licht ik een aantal fouten toe die we als ontwikkelaar nog steeds maken.

Mythes

Periodiek veranderen

Laten we meteen beginnen met de meest hardnekkige mythe. Periodiek veranderen van wachtwoorden helpt niet. Als je je gebruikers gaat dwingen om periodiek een nieuw wachtwoord te kiezen, dan gaan ze simpelweg een reeks gebruiken. Het is dus veel beter om goed te monitoren op potentiële wachtwoord-lekken en evt. ongeautoriseerde toegang. In die gevallen is er tenminste een goede reden voor een wachtwoord-reset.

MyComplexPW1!
MyComplexPW2!
MyComplexPW3!

Complexiteit

Je kent de standaard regeltjes wel: minimaal één hoofdletter, één kleine letter, één cijfer en één leesteken. Maar dan niet die, die en die. Met de meest recente Unicode karakter-set kunnen gebruikers helemaal los gaan met emoji’s, Aziatische, Arabische en andere non-Latin karaktersets en allerhande rare figuurtjes. Zoals hier (4:40) al wordt uitgelegd, een XSS attack-string is een vrij goed wachtwoord, maar wordt vaak niet toegestaan. De bedoeling van al die regels is nobel: we willen onze gebruikers zoveel mogelijk stimuleren om een goed en veilig wachtwoord te gebruiken. Maar wat bereiken we ermee?

Kijk eens naar onderstaande lijst. Dit zijn allerhande varianten van het woord ‘password’ en het aantal keren dat deze werd uitgeprobeerd in een brute-force hack. In totaal werden 49 verschillende varianten geprobeerd.

     17 p455w0rd
     18 p4ssword
     19 p@55w0rd
     27 P@ssw0rd
     29 p@ssword
     32 p4ssw0rd
     88 pa55w0rd
    115 passw0rd
    125 p@ssw0rd
    294 password
Total: 1070

We weten dat gebruikers al snel terugvallen op varianten (l33tsp34k) van bestaande woorden. Uit ervaring weten we ook, dat crackers deze varianten kennen en dit eenvoudig in hun aanvallen en password-dictionaries opnemen. Moeilijke regels wekken dus schijnveiligheid. Daarnaast zorgt de complexiteit ook voor een uitdaging bij gebruikers. Zij worden beperkt in de keuze van hun wachtwoord. Zijn bepaalde speciale tekens wel of niet toegestaan? Zijn er nog hoofdletter / kleine letter eisen? Additionele eisen? Bij de Nederlandse Belastingdienst voor MKB is de eisenlijst al behoorlijk lang en onnodig complex. Bovendien leggen ze beperkingen op, die een gebruiker dwingen om minder sterke wachtwoorden te kiezen. Er is slechts een kleine subset aan speciale karakters mogelijk. De overige eisen zijn eveneens complex en beperkend. Op de eerste regel die je in het plaatje ziet, kom ik later nog terug.

Copy-paste

Sommige websites blokkeren copy-paste acties in het wachtwoord-veld: Stop Password Pasting of SPP. Er zijn een aantal overtuigend klinkende redenen die dit rechtvaardigen. Er is geen enkele reden die dit rechtvaardigt.

Brute-force attacks

Password Pasting zou het mogelijk maken om een geautomatiseerd programma brute-force aanvallen te laten uitvoeren. Door SPP zou deze vorm worden geblokt. De realiteit is, dat brute-force ook zonder het html-form kunnen worden uitgevoerd. Tools als Burp Suite kunnen requests eenvoudig herhalen met andere parameters. SPP stopt dit niet. Bovendien zijn er veel betere manieren om brute-force aanvallen te blokkeren. Denk hierbij aan korte vertragingen na elke inlog-poging en (tijdelijke) lock-outs na een aantal foutieve pogingen.

Slecht onthouden

Door SPP zou een gebruiker vaker het wachtwoord zelf invoeren en daarmee sneller het wachtwoord kunnen onthouden. Bij veelgebruikte websites, waar gebruikers dagelijks op inloggen zou dit misschien nog werken. De meest gevoelige sites, zoals belastingdienst, pensioen, hypotheek, verzekeringswebsites en mogelijk internetbankieren worden echter sporadisch bezocht. Daarnaast wil je de gebruiker zoveel mogelijk complexe, lange en unieke wachtwoorden laten kiezen. Het is onrealistisch om van gebruikers te verlangen dat ze deze unieke wachtwoorden allemaal gaan onthouden. Als strategie is dit dus niet haalbaar.

Clipboard

Wachtwoorden zouden onveilig lang op het clipboard van de gebruiker blijven staan. Als een gebruiker zelf een wachtwoord uit een document copy-paste, dan blijft deze inderdaad op het clipboard staan, totdat het wordt overschreven. De meeste wachtwoord-managers verwijderen echter het wachtwoord, zodra deze is ge-paste. Hiermee wordt de tijd en het risico verkort. Sommige password-managers gebruiken bovendien virtuele keyboards om het copy-paste risico te ontwijken. Bovendien is de machine van een gebruiker met een clipboard-capturing virus of malware al per definitie onbetrouwbaar. Clipboard capturing is dan één van de vele aanvallen die het virus kan proberen en zeker niet het grootst mogelijke probleem.

SPP maakt onveilig

Het blokken van copy-paste helpt dus niet of nauwelijks om systemen veiliger te maken. Het maakt  wachtwoorden juist onveiliger. Password-pasting geeft de gebruiker namelijk belangrijke mogelijkheden. De gebruiker kan eenvoudiger unieke wachtwoorden gebruiken met behulp van een wachtwoord-manager. Deze hebben meestal copy-paste mogelijkheden nodig om te kunnen werken. Copy-paste verlaagt daarnaast de tijd die een gebruiker nodig heeft om in te loggen en voorkomt type-fouten bij complexe en lange wachtwoorden.

Fouten

Tot zover deze mythen die onbewust en vaak onbedoeld tot fouten leiden. De volgende punten zijn echter bekende en nog steeds niet goed gehanteerde regels.

Maximale lengte

We zagen dit al in het voorbeeld van de belastingdienst. Hierbij werd het wachtwoord beperkt tot maximaal 25 karakters. Een goedwillende gebruiker die een extra lang wachtwoord wil instellen, wordt hiermee beperkt. Een langer wachtwoord is niet eens onredelijk, alhoewel slechts zeer weinig gebruikers boven 100 a 200 karakters gebruiken. Mocht je dan ook een maximum willen instellen, neem dan een veel ruimer getal. NIST adviseert minimaal 64 karakters toe te staan. Kosten van opslag zijn dusdanig laag, dat dit geen limiterende factor meer is. We zullen later zien dat beperkingen in lengte niet of nauwelijks nodig zijn, zelfs als je de marginale kosten voor opslag zou mee rekenen.

Mailen van wachtwoord

Bij het aanmaken van een account of de wachtwoord-vergeten functie wordt soms nog het wachtwoord in plain-text naar de gebruiker gemaild. Dit is om meerdere redenen een slecht idee. Ten eerste is e-mail per definitie onveilig, tenzij je PGP gebruikt. Ten tweede geef je met een plain-text wachtwoord in de mail via de wachtwoord-vergeten functie een cruciale fout toe: je kunt het wachtwoord leesbaar herproduceren. Dit kan duiden op het gebruik van een slecht algoritme, bijvoorbeeld door gebruik van encoding (bv. base64) of encryptie terwijl goede hashing gebruikt zou moeten worden. Het kan echter nog slechter:

Plain-text

Geloof het of niet: er worden nog steeds wachtwoorden opgeslagen in plain-text. Dat plain-text storage fout is, mag duidelijk zijn. Het is echter lastig en complex om de opslag van wachtwoorden echt goed te doen. Encoding, encryptie, hashing, als je deze termen niet vlekkeloos en in detail kunt toelichten, dan zul je er gegarandeerd fouten mee maken.

Encoding

Bijna net zo makkelijk leesbaar als plain-text is encoding. Er zijn meerdere gevallen bekend waarbij de wachtwoorden in Base64 encoding zijn opgeslagen. Deze en andere encoding kan eenvoudig worden omgekeerd. Een eigen programma maken om dit offline uit te voeren kost weinig moeite. Encoding werkt twee richtingen op, het is omkeerbaar. En dat is bij wachtwoorden niet nodig.

Encryptie

Ook encryptie alleen is niet voldoende om wachtwoorden veilig op te slaan. Net als encoding is encryptie omkeerbaar, hoewel je soms het encryptie-mechanisme en mogelijk de sleutel moet weten om dit uit te kunnen voeren. Bij zwakkere encryptie kunnen met decryptie de originele wachtwoorden worden achterhaald.

Hashing

De enige manier om wachtwoorden te bewerken zodat ze niet tot het originele wachtwoord te herleiden zijn, is hashing en wel met een sterk hashing-mechanisme. Je kunt onmogelijk een hash terugrekenen naar het originele wachtwoord. Wat een aanvaller wel zou kunnen doen, is een lange lijst maken met bekende wachtwoorden en van elk de hash zelf uit rekenen. Door deze zelfgemaakte lijst te vergelijken met de hashes uit een gestolen database, kan een aanvaller eenvoudig de oorspronkelijke wachtwoorden opzoeken. Soortgelijke lijsten circuleren al op internet en worden ook wel rainbow-tables genoemd.

Salt

Een verdediging tegen rainbow-tables is het gebruik van salt. Een salt combineert het wachtwoord met een deel van de andere gegevens van de gebruiker om een complexere string te maken. Bij een inlog-poging kan de invoer weer worden gecombineerd met dezelfde gegevens (bv. de achternaam van de gebruiker) en vergeleken met de database-waarde. Als de aanvaller echter beschikking over de database heeft, dan heeft hij ook de beschikking over de gegevens die gebruikt worden voor het salten. Hij hoeft enkel te achterhalen welke gegevens als salt worden gebruikt.

Pepper

Ook hier is weer een verdediging voor te bedenken. Een unieke en lange geheime sleutel wordt gebruikt om het wachtwoord opnieuw te hashen. Deze sleutel wordt vaak pepper of HMAC genoemd. De methode die hierbij gebruikt wordt is lastig uit te leggen en valt meer onder cryptografie. Een vereenvoudigde uitleg van pepper is als een salt die voor iedere gebruiker hetzelfde is, maar die wel buiten de database liefst in een veilige omgeving (vault) wordt opgeslagen.

Hardening

Zelfs als je bovenstaande maatregelen correct implementeert kan een aanvaller nog altijd proberen om simpelweg alle mogelijke wachtwoorden te proberen: brute-force. Een verdediging tegen brute-forcing kan door ervoor te zorgen dat de encryptie bij een password check of “omzetting” memory en cpu intensief is. Hierdoor wordt de tijd en hardware die nodig is om de wachtwoorden te kraken flink verhoogd. 

Door eerst te hashen met SHA2-512 wordt het wachtwoord, ongeacht de lengte omgezet in een 512 bits hash. Hierdoor kan elk wachtwoord van arbitraire lengte worden opgeslagen in veld van 64 karakters. Hiermee hoeven we dus geen extra opslagruimte te reserveren voor extra lange wachtwoorden. Door het vervolgens te combineren met een salt van 8 karakters kunnen we deze encrypten met bcrypt. Een klein nadeel van bcrypt is namelijk de limiet van 72 karakters. Andere opties naast bcrypt zijn scrypt, PBKDF2 en argon2.

Wiel uitvinden

In veel organisaties wordt functionaliteit zelf ontwikkelt. Bestaande pakketten sluiten vaak niet precies aan op de behoefte van de organisatie. Ook voor identity management en authenticatie wordt dan al snel een eigen oplossing gebouwd. De complexiteit en gemis aan zeer specialistische kennis zorgt dan al snel voor fouten in de software. Dit artikel is nog beperkt tot enkel wachtwoorden. Er valt nog een hoop te schrijven over account initialisatie, privacy, rechtenbeheer, beveiliging etc. Er zijn ontzettend veel mogelijkheden om fouten te maken. En een enkele fout kan funest zijn. Gelukkig hoef je als ontwikkelaar niet alles zelf te doen. De meeste frameworks hebben uitgebreide mogelijkheden om account en identity management op te zetten. Koppeling met externe identity providers zoals Google of Facebook, zijn vaak ingebakken of als plugin beschikbaar. En er zijn vaak mogelijkheden om het account management naar wens aan te passen. Er zijn dus weinig tot geen redenen om zelf opnieuw het wiel uit te vinden.

Frameworks en moderne wachtwoord policy

De regels van vroeger zijn dus achterhaald. Beperkingen in de wachtwoord-policy werken averechts. Je kunt ze dus beter weglaten. Daarnaast is goede en veilige opslag van wachtwoorden een complex probleem. Er is diepgaande specialistische kennis voor nodig om dit goed te doen. Gelukkig zijn er voldoende standaard oplossingen die hier speciaal voor ontwikkeld zijn. Gebruik deze dan ook. Een framework als Spring Security helpt je als ontwikkelaar om goed en veilig identity management op te zetten.

Het bericht Wachtwoorden: 3 mythes, 4 fouten en 5 tips voor ontwikkelaars (deel 1) verscheen eerst op First8 Java Consultancy.

]]>
Wachtwoorden: 3 mythes, 4 fouten en 5 tips voor gebruikers https://technology.first8.nl/wachtwoorden-3-mythes-4-fouten-en-5-tips-voor-gebruikers/ Thu, 08 Feb 2018 11:27:14 +0000 https://technology.first8.nl/?p=6065 Wachtwoorden…. nog steeds het belangrijkste authenticatie mechanisme: het bewijs dat je bent wie je zegt dat je bent. We hebben er bovendien tientallen, zo niet honderden. Allemaal met andere eisen, andere geldigheidsduur. Ze beschermen (hopelijk) gegevens van vrij onbelangrijk tot zeer persoonlijk en gevoelig. We (denken te) weten wat de adviezen zijn voor wachtwoorden. Maar we volgen die nog steeds … Lees verder Wachtwoorden: 3 mythes, 4 fouten en 5 tips voor gebruikers

Het bericht Wachtwoorden: 3 mythes, 4 fouten en 5 tips voor gebruikers verscheen eerst op First8 Java Consultancy.

]]>
Wachtwoorden…. nog steeds het belangrijkste authenticatie mechanisme: het bewijs dat je bent wie je zegt dat je bent. We hebben er bovendien tientallen, zo niet honderden. Allemaal met andere eisen, andere geldigheidsduur. Ze beschermen (hopelijk) gegevens van vrij onbelangrijk tot zeer persoonlijk en gevoelig. We (denken te) weten wat de adviezen zijn voor wachtwoorden. Maar we volgen die nog steeds niet allemaal. Zijn die adviezen eigenlijk wel zo goed? Hoe kunnen we met al die adviezen toch op een normale manier onze gegevens veilig stellen?

Als Senior Java Developer bij First8 houd ik me tijdens en buiten werktijden bezig met security en privacy. Na te lang zelf gestoeid te hebben (en nog steeds) met de beperkingen van websites en applicaties op gebied van wachtwoorden, vond ik het tijd om een artikel te schrijven die voor eens en altijd afrekent met de mythes en valkuilen van wachtwoorden. Dat worden er uiteindelijk twee, gericht op gebruikers in dit artikel en ontwikkelaars in het volgende deel.

Zoals de titel al zegt, ga ik eerst een drie-tal mythes bespreken. Daarna stip ik vier fouten aan, die de meeste mensen nog steeds maken. Tenslotte geef ik vijf tips, die je zelf kunt toepassen om veel veiliger om te kunnen gaan met wachtwoorden en beveiligde accounts. Als er één advies is dat je in ieder geval wilt opvolgen, dan is dat het gebruik van een wachtwoord-manager. Hiermee volg je meteen een aantal zeer belangrijke tips op, die ik hieronder zal toelichten.

Drie mythes

Om te beginnen: bepaalde adviezen van oudsher zijn achterhaald. Ze zijn gewoonweg extreem gebruikers-onvriendelijk. Je weet wat er dan gebeurt; we vinden een work-around. De vindingrijkheid van mensen wordt nog steeds zwaar onderschat. Overijverige systeembeheerders bedenken nog steeds beveiligingsmaatregelen die in de praktijk juist averechts werken.

1: Periodiek veranderen

Verander je regelmatig je wachtwoord? Waarom? Omdat het moet hè? Elk jaar, elke drie maanden of misschien zelfs elke maand. Wat doe je dan? Je hoogt het nummertje in je wachtwoord op met 1. Dat is ontzettend handig als je vergeten bent wanneer je ook al weer in dienst kwam. Deel het getal door 4 en je hebt het aantal jaar dat je in dienst bent. 

MyComplexPW1!
MyComplexPW2!
MyComplexPW3!

Je snapt, op deze manier is het vrij makkelijk voor een hacker om je volgende wachtwoord te raden. Bovendien: ook al zou je als gebruiker een compleet ander wachtwoord instellen, als je wachtwoord is gehackt of geraden, dan nog heeft een hacker tot maximaal 1 a 12 maanden om in je account rond te neuzen. Een verplichte periodieke wachtwoord-aanpassing heeft dus simpelweg geen zin.

2: Complexe wachtwoorden

Je wachtwoord is complex en met reden. Die reden is meestal omdat het systeem dat van je verlangt. Maar hoe goedbedoeld die reden ook is, de gevolgen gaan volledig aan het doel voorbij. Want wat doe je als je een wachtwoord moet bedenken en je wordt bestookt met allerhande moeilijke eisen? Dan ga je letters vervangen voor cijfers, leestekens etc. Alsof een password-hacker niet kon bedenken dat de letter A ook in de @ zit, of lijkt op een 4. Bovendien vervangt zowat iedereen, niet alleen de jeugd, inmiddels de E voor een 3, de I voor een 1 of een !, de O voor de 0, etc. Het mag dan ook geen verrassing zijn, dat deze eisen helemaal geen toegevoegde waarde meer hebben. Als ik zeg “correct horse battery staple”, dan weten de meeste techies meteen waar ik op doel. Als je de comic hiernaast aanklikt, dan weet jij het vanaf nu ook. Overigens is ook deze theorie inmiddels achterhaald en dat heeft dan weer met dictionary attacks te maken. Het gaat iets te ver voor dit artikel, maar ik ga je hieronder helpen om je ook tegen deze aanval te beschermen.

3: Onthouden

Onthoud jij al je wachtwoorden? Nee hè? Dat dacht ik al. Dat is maar goed ook. Het is natuurlijk onbegonnen werk, met al die accounts die we tegenwoordig hebben. Privé-mail, werk-mail, DigiD, werk-computer, je eigen computer, verschillende accounts op Social media, de nodige webwinkels, natuurlijk je internet-bankieren en wat al niet meer. Langzaam maar zeker komt bij bedrijven het besef dat het anders moet. Social logins zoals login met je Facebook of Google account zijn een optie. Maar ook die brengen weer een risico. Als je Facebook account is gehackt, dan zijn alle accounts die je daaraan gekoppeld hebt in één keer mee gekaapt.

Vier Fouten

Het kan vrij lastig zijn om verkeerde adviezen af te leren. Hopelijk heb ik je hierboven kunnen overtuigen hoe achterhaald deze rare mythes zijn. Maar het is nog veel moeilijker om de adviezen die je kent, netjes en consistent te hanteren. De systemen van oudsher dwingen ons in onwerkbare patronen. Zoals gezegd: dan gaan we oplossingen bedenken die het enigszins werkbaar maken. De bekende post-it op de monitor is daar een voorbeeld van. Maar er zijn meer “oplossingen”, die je echt beter kunt mijden. Hieronder geef ik aan waarom ze zo ernstig zijn en ook wat de betere oplossingen zijn.

1: Hergebruiken

Met zoveel accounts is het bijhouden van al die wachtwoorden praktisch onbegonnen werk. Waarom is het hergebruik van die wachtwoorden dan zo’n probleem? En als er eentje op straat komt te liggen, dan kan ik ze toch gewoon weer veranderen? Wat boeit het nou, als iemand je account van Bol.com kan inzien? Als ze iets bestellen, dan betaal je het toch gewoon niet? Je hebt niets te verbergen, toch? Ik weet wel zeker dat je je vergist. Maar veel belangrijker, als je overal hetzelfde wachtwoord gebruikt, dan zijn je accounts zo zwak beveiligd als de zwakste schakel. Dus heeft die ene simpele katten-site het niet op orde en lekt je wachtwoord via die site uit, dan is de inlog op je bankrekening ook gekaapt. Maar beveiliging kan ook bij grote bedrijven niet goed genoeg op orde zijn. Zelfs sites als LinkedInYahoo, de ECB en andere banken en financiële instellingen worden nog steeds gehackt.  Dus doe jezelf een groot plezier en gebruik je wachtwoorden slechts 1x per site.

2: Opschrijven

Nu de uitdaging: hoe ga je die grote wachtwoorden-brei nu werkbaar maken? Het zijn zo gigantisch veel accounts, dat alleen de gebruikersnaam onthouden al een uitdaging is. Dus je gaat het opschrijven. Toegeven, als je moet kiezen tussen hergebruik of unieke en sterke wachtwoorden die je opschrijft, dan is dat laatste nog altijd de minst erge optie. Dus mocht je niet handig genoeg zijn met computers, gebruik dan een wachtwoord notitie-blok en bewaak die met je leven. Maar waarschijnlijk lees je dit blog, omdat je aardig wat computer-ervaring hebt. In dat geval zijn er veel betere opties dan opschrijven.

3: Onthouden

Heb je een fotografisch geheugen? Gefeliciteerd. Maar kun je ook 100 unieke wachtwoorden met elk 60 verschillende tekens (inclusief leestekens) onthouden? Nee, dat dacht ik al. Daarom is mijn advies, stop met onthouden van wachtwoorden. Het heeft geen zin, het is onbegonnen werk en nogmaals, er is een veel betere manier. 

4: Lengte

Hoe lang is je langste wachtwoord? Minder dan 20 tekens? Iedereen weet, hoe langer het wachtwoord, hoe moeilijker te kraken. Toch maken we onze wachtwoorden niet lang genoeg. Sterker nog, we kunnen het soms niet eens, al zouden we het willen. Hiernaast een screenshot van het wachtwoord scherm van de Belastingdienst. Kijk maar eens goed naar de draconische regels die je voor je kiezen krijgt. In het volgende deel voor developers zal ik hier uitgebreid op ingaan. Het belangrijkste punt voor nu is echter de lengte. De eerste hint geeft aan dat het wachtwoord minimaal 8 en maximaal 25 tekens mag bevatten. Waarom dat maximum? Er is geen enkele goede reden om de lengte van het wachtwoord te beperken tot 25 tekens. Een maximum van 100 a 200 tekens zou veel beter zijn. Dat geeft jou als gebruiker veel betere mogelijkheden om een sterk wachtwoord te kiezen.

Vijf tips

Voor nu even genoeg misvattingen en fouten om af moeten leren. Wat natuurlijk belangrijk is voor jou als gebruiker is hoe je jezelf wel goed kunt beschermen. Wat zijn nou de maatregelen die echt zin hebben? Hieronder staan vijf tips die je als gebruiker kunt toepassen om je accounts telkens een stuk veiliger te maken. Er is nog steeds geen 100% veiligheid, maar met deze maatregelen ben je als “normale” gebruiker vaak prima beschermd. Vergelijk het met de beveiliging van je huis. Als het heel veel moeite kost voor een inbreker om in te breken, dan zal hij al snel een ander huis kiezen. Tenzij die inbreker zeker weet dat er ook echt een grote buit te halen is. 

1: Lang

Gebruik lange wachtwoorden. Zoals je in de bovenstaande strip al kon lezen, hoe langer het wachtwoord, hoe meer tijd het kost om het te raden. Ga voor wachtwoorden van minimaal 30 tekens. Ik zelf probeer altijd een wachtwoord van 30 tekens (met onderstaande maatregelen erbij) en kort dit alleen in als het moet. Zoals bij de belastingdienst dus. 

2: Complex

Hoe complexer je wachtwoord, hoe beter. Dus gebruik alle mogelijke tekens die je kunt vinden. Wist je dat emoticons tegenwoordig ook officiële geldige tekens zijn? Yep, de Unicode karakter-set is nu uitgebreid met Emoji. De eerste Emoji-karakters zaten daar al in sinds 2015, maar sinds 2017 kun je met gemak het complete eerste couplet van Let it go (uit de film Frozen) als wachtwoord invoeren.

The ❄️ 🌟 🔦 ⚪ on the mountain 🌙 🌠. 🙅🏻 a👣 to 🐝 👀. A 🏰 of 😢, and it 👀 like☝️ the 👑. 
The 💨 is 🐺 like this 🌀 ❄️ ☔️ 🏠. 🙅🏻 keep it in, ☁️ 💡 ☝️ tried.

Overigens zou ik een ander liedje kiezen. Dit couplet is namelijk al eens als wachtwoord gebruikt. Het werd uitgebreid op internet besproken toen de gebruiker erachter kwam, dat hij niet meer in zijn Macbook kon inloggen. 

3: Uniek

Het kan niet vaak genoeg gezegd worden: gebruik altijd unieke wachtwoorden. Zo beveilig je jezelf als er één van de website waar jij gebruik van maakt, wordt gehackt. Het uitlekken van je wachtwoord heeft dan geen sneeuwbal-effect, want de schade stopt bij die ene website. Het maakt niet uit, dat je dan tientallen wachtwoorden hebt. Daar hebben we de volgende tip voor.

4: Wachtwoord-manager

Gebruik een wachtwoord-manager. Je zult er echt even wat moeite voor moeten doen. Maar het helpt je zo ongelooflijk veel. Je kunt hiermee met gemak hele lange, complexe wachtwoorden instellen en bovendien voor elk account een uniek wachtwoord. Het is vaak zelfs mogelijk om nieuwe (unieke, lange en complexe) wachtwoorden te laten genereren, afhankelijk van de opties die jij aangeeft. Er is één heel belangrijke voorwaarde waar je zelf voor moet zorgen: een hoofd-wachtwoord. De wachtwoorden in je wachtwoord-manager worden versleuteld met dat hoofd-wachtwoord. Zonder dat wachtwoord kun je er dus niet in. Dat is heel goed, want dan kan een hacker er namelijk ook niet in. Let wel: er is voor dat hoofd-wachtwoord geen backup-optie. Er is geen “Ik ben mijn wachtwoord vergeten”-service. Onthoud dat wachtwoord dus alsof je leven ervan afhangt. Kies daarbij absoluut een lang en moeilijk niet te raden wachtwoord.

Keuze te over

Er zijn trouwens vele wachtwoord-managers. Daarin zul je nog wat huiswerk moeten doen. Je zult een keuze moeten maken of je je wachtwoorden automatisch wilt synchroniseren via internet, de zogenaamde “Cloud-sync”. Daarmee kun je eenvoudig wachtwoorden die je op je thuis-computer hebt ingevoerd gebruiken op je werk-computer en je telefoon. Let wel: in dat geval is je hoofdwachtwoord natuurlijk super-belangrijk. Als je wachtwoorden-bestand wordt onderschept of gekopieerd van één van je apparaten, dan is dat hoofdwachtwoord je laatste verdediging voor al je accounts. Zelf gebruik ik deze functie bewust niet. Als ik de wachtwoorden wil synchroniseren, dan doe ik dat zelf en handmatig. Ik gebruik een USB-stick of kabeltje om het bestand over te hevelen. Hierdoor kan het ook niet via e-mail, wifi of LAN-netwerk onderschept worden.

Automatisch of niet

De tweede keuze die je moet maken is of je automatische invul-functies wilt inschakelen. Er zijn voor de meeste wachtwoord-managers ook browser-uitbreidingen die het wachtwoord op de juiste site automatisch voor je invullen. Ook hierbij kies ik zelf voor wat minder gebruiksgemak, maar wel een nog betere bescherming. In dat geval moet ik dus om in te loggen even schakelen tussen mijn wachtwoord-manager en de browser of applicatie waar ik wil inloggen. Het minimale risico van een lek of misbruik in de browser-plugin sluit ik daarmee uit. 

Er wordt door security-experts heel kritisch gekeken naar wachtwoord-managers en met reden. Als er een bug of lek ontstaat of op enig andere manier iets fout gaat bij het gebruik, dan liggen meteen al je accounts op straat. Er zijn dan ook nog heel wat punten waarop je je keuze voor een bepaalde wachtwoord-manager kunt baseren. Mocht je je willen verdiepen, dan is hier meer informatie over wachtwoord-managers. Er worden heel wat technische termen gebruikt. Maar als je je wilt verdiepen kun je jezelf nog beter beveiligen. Mocht dit te technisch worden, kies dan gerust voor een wachtwoord-manager als 1Password, LastPass of KeePass.

5: Twee factor authenticatie

Wil je na deze tips nog meer veiligheid? Dat kan. Stel één van je wachtwoorden lekt alsnog uit, is die account dan meteen verloren? Dat hangt ervan af. Je gebruikt waarschijnlijk bij internet-bankieren al zo’n raar apparaatje. Dat apparaatje is naast je wachtwoord de tweede factor die jouw identiteit bevestigt. Het gebruik van dit soort apparaatjes of apps wordt 2-factor authenticatie genoemd, kortweg 2FA.

Het voordeel hiervan is dat een hacker met alleen je gebruikers-naam en wachtwoord nog steeds (bijna) niets kan doen. Hij kan hooguit de gegevens op je rekening bekijken, maar geen geld overboekingen. Afhankelijk van hoe een website 2FA gebruikt, kan er dus weinig of niets misgaan. Helaas is 2FA nog lang niet overal beschikbaar. De grote websites als Google, Facebook, Twitter en natuurlijk banken gebruiken allemaal 2FA, optioneel of verplicht. Maak er dan ook gebruik van. Vaak kun je ook aanvinken dat je werkt op een vertrouwde computer, zodat je jezelf maar éénmalig of periodiek hoeft te identificeren met 2FA. 

Gebruik ook het liefst een apparaatje, zoals banken die voor je aanleveren. Andere opties zijn autorisatie-apps, zoals Authy of Google Authenticator, maar ook SMS-berichten worden nog gebruikt. Die laatste is de minst veilige optie, aangezien ook SMS onderschept kan worden. Het is echter nog altijd veiliger dan zonder 2FA.

Tools & 2FA dus

Er zijn dus heel wat maatregelen die je zelf kunt nemen om je accounts te beveiligen. Niet alle maatregelen zijn altijd toepasbaar, gezien de beperkingen van sommige websites of applicaties. Kies dus telkens de beste combinatie van maatregelen en probeer zoveel mogelijk opties uit. Als er iets is wat je minimaal zou moeten doen, dan is het een wachtwoord-manager gebruiken. Daarmee kun je meteen op een gemakkelijke manier complexe, lange en vooral unieke wachtwoorden gebruiken voor al je accounts. Als je waar mogelijk ook nog 2FA toepast, dan ben je eigenlijk al heel goed beschermd.

Volgende keer ga ik dieper in op de technische maatregelen die ontwikkelaars kunnen treffen om het jou als gebruiker makkelijk te maken en je kunnen sturen naar een veilig wachtwoord. Daarnaast zal ik ook een aantal belangrijke fouten toelichten, die zowel de veiligheid als de privacy van gebruikers in gevaar kunnen brengen.

Het bericht Wachtwoorden: 3 mythes, 4 fouten en 5 tips voor gebruikers verscheen eerst op First8 Java Consultancy.

]]>
Functional Java by Example | Part 3 – Don’t Use Exceptions to Control Flow https://technology.first8.nl/functional-java-by-example-part-3-dont-use-exceptions-to-control-flow/ Fri, 19 Jan 2018 10:25:59 +0000 https://technology.first8.nl/?p=6056 This is part 3 of the series called “Functional Java by Example” and is a cross-post from my personal blog. The example I’m evolving in each part of the series is some kind of “feed handler” which processes documents. In previous parts I started with some original code and applied some refactorings to describe “what” instead of “how”. In order … Lees verder Functional Java by Example | Part 3 – Don’t Use Exceptions to Control Flow

Het bericht Functional Java by Example | Part 3 – Don’t Use Exceptions to Control Flow verscheen eerst op First8 Java Consultancy.

]]>
This is part 3 of the series called “Functional Java by Example” and is a cross-post from my personal blog.

The example I’m evolving in each part of the series is some kind of “feed handler” which processes documents. In previous parts I started with some original code and applied some refactorings to describe “what” instead of “how”.

In order to help the code going forward, we need to get rid of the good ol’ java.lang.Exception. (disclaimer: we can’t actually get rid of it) That’s where this part comes in.

If you came here for the first time, it’s best to start reading from the beginning. It helps to understand where we started and how we moved forward throughout the series.

These are all the parts:

I will update the links as each article is published. If you are reading this article through content syndication please check the original articles on my blog.

Each time also the code is pushed to this GitHub project.

Getting up to speed about Exceptions

Our java.lang.Exception has been around since Java 1.0 – and has basically been our friend in good times and nemesis at other times.

There’s not much to talk about them, but if you want to read up on a few sources, here are my favorites:

You on Java 8 already? Life became so much better! I… Err…oh, wait.

Ok, seems that there’s no way you can actually do it right.

At least, after reading above list, we’re now completely up-to-speed on the topic 🙂

Luckily I don’t have to write a blog post any more about what’s been covered for 95% already in above articles, but I’ll focus here on the one Exception we actually have in the code 🙂

Side effects

Since you’re reading this post, you’re probably interested in why this all has to do with functional programming.

On the road to approaching your code in a more “functional way”, you may have encountered the term “side effect” and that it’s a “bad thing”.

Ted-Vinke-Functional-Programming-Side-Effects

In the real world, a side effect is something you did not intend to happen, and you might say it’s equivalent to an “exceptional” situation (you would indicate with an exception), but it has a more strict meaning in a Functional Programming context.

The Wikipedia-article about a Side effect says:

Side effect (computer science) In computer science, a function or expression is said to have a side effect if it modifies some state outside its scope or has an observable interaction with its calling functions or the outside world besides returning a value. … In functional programming, side effects are rarely used.

So let’s see how our FeedHandler code currently looks like after the first two articles in this series:

class FeedHandler {

  Webservice webservice
  DocumentDb documentDb

  void handle(List<Doc> changes) {

    changes
      .findAll { doc -> isImportant(doc) }
      .each { doc ->

      try {
        def resource = createResource(doc)
        updateToProcessed(doc, resource)
      } catch (e) {
        updateToFailed(doc, e)
      }
    }
  }

  private Resource createResource(doc) {
    webservice.create(doc)
  }

  private boolean isImportant(doc) {
    doc.type == 'important'
  }

  private void updateToProcessed(doc, resource) {
    doc.apiId = resource.id
    doc.status = 'processed'
    documentDb.update(doc)
  }

  private void updateToFailed(doc, e) {
    doc.status = 'failed'
    doc.error = e.message
    documentDb.update(doc)
  }

}

There’s one place where we try-catch exceptions, and that’s where we loop through the important documents and try to create a “resource” (whatever that is) for it.

try {
  def resource = createResource(doc)
  updateToProcessed(doc, resource)
} catch (e) {
  updateToFailed(doc, e)
}

In code above catch (e) is Groovy shorthand for catch (Exception e).

Yes, that’s the generic java.lang.Exception which we’re catching. Could be any exception, including NPE.

If there’s no exception thrown from the createResource method, we update the document (“doc”) to ‘processed’, else we update it to ‘failed’. BTW, even updateToProcessed can throw an exception too, but for the current discussion I’m actually only interested in a successful resource creation.

So, above code works (I’ve got the unit tests to prove it :-)) but I’m not happy with the try-catch statement as it is now. I’m only interested in successful resource creation, and, silly me, I could only come up with createResource either returning a successful resource or throwing an exception.

Throwing an exception to signal something went wrong, get the hell out of dodge, have caller catch the exception in order to handle it, is why exceptions were invented right? And it’s better than returning null right?

It happens all the time. Take some of our favorite frameworks, such as EntityManager#find from the JPA spec:

Ted-Vinke-FP-Exceptions-JPA-find

Arg! Returns null.

Returns:
the found entity instance or null if the entity does not exist

Wrong example.

Functional Programming encourages side-effect free methods (or: functions), to make the code more understandable and easier to reason about. If a method just accepts certain input and returns the same output every time – which makes it a pure function – all kinds of optimizations can happen under the hood e.g. by the compiler, or caching, parallelisation etc.

We can replace pure functions again by their (calculated) value, which is called referential transparancy.

In previous article, we’ll already extracted some logic into methods of their own, such as isImportant below. Given the same document (with the same type property) as input, we’ll get the same (boolean) output every time.

boolean isImportant(doc) {
  doc.type == 'important'
}

Here there’s no observable side effect, no global variables are mutated, no log file is updated – it’s just stuff in, stuff out.

Ted-Vinke-Functional-Data-In-Data-Out

Thus, I would say that functions which interact with the outside world through our traditional exceptions are rarely used in functional programming.

I want to do better than that. Be better. 🙂

Optional to the rescue

As Benji Weber expresses it:

There are different viewpoints on how to use exceptions effectively in Java. Some people like checked exceptions, some argue they are a failed experiment and prefer exclusive use of unchecked exceptions. Others eschew exceptions entirely in favour of passing and returning types like Optional or Maybe.

Ok, let’s try Java 8’s Optional so signal whether a resource can or can not be created.

Let’s change the our webservice interface and createResource method to wrap and return our resource in an Optional:

//private Resource createResource(doc) {
private Optional<Resource> createResource(doc) {
  webservice.create(doc)
}

Let’s change the original try-catch:

try {
  def resource = createResource(doc)
  updateToProcessed(doc, resource)
} catch (e) {
  updateToFailed(doc, e)
}

to map (processing resource) and orElseGet (processing empty optional):

createResource(doc)
  .map { resource ->
    updateToProcessed(doc, resource)
  }
  .orElseGet { /* e -> */
    updateToFailed(doc, e)
  }

Great createResource method: either correct result comes back, or an empty result.

Wait a minute! The exception e we need to pass into updateToFailed is gone: we have an empty Optional instead. We can’t store the reason why it failed — which we do need.

May be an Optional just signals “absence” and is a wrong tool for our purpose here.

Exceptional completion

Without the try-catch and with the map-orElseGet instead, I do like the way the code started to reflect the “flow” of operations more. Unfortunately, using Optional was more appropriate for “getting something” or “getting nothing” (which names like map and orElseGet also suggested) and didn’t give us the opportunity to record a reason for failing.

What’s another way to either get the successful result or get the reason for failing, still approaching our nice way of reading?

A Future. Better yet: a CompletableFuture.

A CompletableFuture (CF) knows how to return a value , in this way it’s similar to an Optional. Usually a CF is used for getting a value set in the future, but that’s not what we want to use it for…

From the Javadoc:

A Future that …, supporting … actions that trigger upon its completion.

Jip, it can signal “exceptional” completion — giving me the opportunity to act upon it.

Let’s change the map and orElseGet:

createResource(doc)
  .map { resource ->
    updateToProcessed(doc, resource)
  }
  .orElseGet { /* e -> */
    updateToFailed(doc, e)
  }

to thenAccept (processing success) and exceptionally (processing failure):

createResource(doc)
  .thenAccept { resource ->
    updateToProcessed(doc, resource)
  }
  .exceptionally { e ->
    updateToFailed(doc, e)
  }

The CompletableFuture#exceptionally method accepts a function with our exception e with the actual reason for failure.

You might think: tomayto, tomahto. First we had try-catch and now we have thenAccept-exceptionally, so what’s the big difference?

Well, we can obviously not get rid of the exceptional situations, but we’re now thinking like a resident of Functionalville would: our methods start to become functions, telling us something goes in and something goes out.

Consider it a small refactoring we need towards part 4, limiting the amount of side effects in our code even more, and part 5.

This is it for now 🙂

For reference, here’s the full version of the refactored code.

class FeedHandler {

  Webservice webservice
  DocumentDb documentDb

  void handle(List<Doc> changes) {

    changes
      .findAll { doc -> isImportant(doc) }
      .each { doc ->
        createResource(doc)
        .thenAccept { resource ->
          updateToProcessed(doc, resource)
        }
        .exceptionally { e ->
          updateToFailed(doc, e)
        }
      }
  }

  private CompletableFuture<Resource> createResource(doc) {
    webservice.create(doc)
  }

  private boolean isImportant(doc) {
    doc.type == 'important'
  }

  private void updateToProcessed(doc, resource) {
    doc.apiId = resource.id
    doc.status = 'processed'
    documentDb.update(doc)
  }

  private void updateToFailed(doc, e) {
    doc.status = 'failed'
    doc.error = e.message
    documentDb.update(doc)
  }

}

This is a cross-post from my personal blog. Follow me on @tvinke if you like what you’re reading or subscribe to my blog on https://tedvinke.wordpress.com.

Het bericht Functional Java by Example | Part 3 – Don’t Use Exceptions to Control Flow verscheen eerst op First8 Java Consultancy.

]]>
5 overeenkomsten tussen Lego en First8 https://technology.first8.nl/5-overeenkomsten-lego-first8/ Fri, 05 Jan 2018 07:00:31 +0000 https://technology.first8.nl/?p=5991 Uitdaging Afgelopen december hebben we met onderstaande groep bij First8 een zaterdag besteed aan een mooie uitdaging. We bouwden het grootste, meest complexe en uitdagende Lego model dat verkrijgbaar is: de nieuwe Millennium Falcon, model 75192 uit de Star Wars Ultimate Collector Series. Een monster-model van 7541 steentjes en een gewicht van meer dan 13 kg!  De Falcon bouw je … Lees verder 5 overeenkomsten tussen Lego en First8

Het bericht 5 overeenkomsten tussen Lego en First8 verscheen eerst op First8 Java Consultancy.

]]>
Uitdaging

Afgelopen december hebben we met onderstaande groep bij First8 een zaterdag besteed aan een mooie uitdaging. We bouwden het grootste, meest complexe en uitdagende Lego model dat verkrijgbaar is: de nieuwe Millennium Falcon, model 75192 uit de Star Wars Ultimate Collector Series. Een monster-model van 7541 steentjes en een gewicht van meer dan 13 kg! 

Lego UCS Millennium Falcon in doos met Arjan en Bram

De Falcon bouw je niet even tussendoor. Dit model is het meest uitdagende model ooit door Lego uitgebracht: de instructies tellen meer dan 450 pagina’s! Met goed teamwork, de juiste expertise, doorzettingsvermogen en een goede voorbereiding hebben we dit model in een recordtijd van 8 uur opgebouwd.
Bij First8 zijn we vlot teamwork gewend. Wij zoeken projecten met uitdaging en passie. We houden van opdrachten die ons verder brengen. Opdrachten die onze kennis en ervaring op de proef stellen en aansprekende resultaten opleveren. En dit bouw-project is nagenoeg gelijk aan de bedrijfscultuur van First8, onze medewerkers en de uitdagingen die we in ons dagelijkse werk aangaan. In deze blog vertellen we graag wat over de overeenkomsten met het Lego bouwproces.

 

Reden 1: Goede voorbereiding

Opstelling van tafel als werkblad met camera op statiefVoor dit bouwproject hebben we goede voorbereidingen getroffen. We verwachtten een bouwtijd van minstens 10 uur. Dit zou zowel van de bouwers als de camera-apparatuur een flink uithoudingsvermogen vergen. Daarnaast zijn we in alle vroegte begonnen, zodat we het daglicht konden gebruiken bij de opnamen. Voor de bouwers hebben we genoeg drinken en lunch geregeld, met de mogelijkheid om ook avondeten te regelen indien nodig. We hebben de grootst mogelijke tafel als werkruimte gekozen die we beschikbaar hebben. En als laatste hebben we een digitale bouwbeschrijving gebruikt op laptops om ruimte te besparen en om parallel te kunnen bouwen.

Bij First8 gaan we goed beslagen te werk. We laten niets aan het toeval over. Een project begint al ruim voordat we de eerste regel code hebben gezet. We verdiepen ons in onze projectpartner, zodat we helder hebben wat zijn motivatie is. We bijten ons vast in het vraagstuk en willen het onderste uit de kan weten. Dit helpt ons het probleem beter te doorgronden en dwingt onze partner om goed, beter, best na te denken en alles helder te verwoorden.

 

Reden 2: Duurzame basis

Gedeeltelijk gebouwde Millennium Falcon, waarvan het  raamwerk wel al afgebouwd isDe Millennium Falcon weegt ongeveer 13 kg en dat moet geen probleem zijn. Lego steentjes kunnen namelijk heel wat gewicht aan. Maar liefst 432 kg is nodig om een standaard 2×2 blok te vervormen. Voor bouwwerken van 13 kg is echter ook stabiliteit nodig. Bij dit model is daar goed over nagedacht. Het raamwerk van Lego Technic blokken vormt de basis. Onze ervaren bouwers hebben deze basis gebouwd, zodat we daarop konden voortbouwen met mensen van allerlei niveau’s.

Onze projecten gaan ook zo. We beginnen direct goed, zodat we niet voor verrassingen komen te staan. Voor de fundering zetten we dan ook ervaren mensen in, die verstand van zaken hebben. Alleen dan kunnen we zeker zijn van een duurzaam en betrouwbaar eindresultaat.

 

 

Reden 3: Gemengde teams

Kinderen werken met Lego Mindstorms programmeerset
Devoxx4kids oktober 2017
Nadat we de basis goed hadden neergezet, hebben we ons bouwteam opgeschaald met mensen van allerhande ervaringsniveaus. Daarin heeft iedereen zijn of haar eigen kracht. Laura heeft bijvoorbeeld veel ervaring met verschillende bouwtechnieken. Ze is gewend om te improviseren en bouwt altijd met grote hoeveelheden Lego. Bram heeft veel ervaring met Star Wars Ultimate Collector Series modellen. Hij heeft bovendien de eerste Millennium Falcon (model 10179) al enkele malen gebouwd. Arjan is dan weer een ervaren Lego bouwer in Star Wars USC modellen en complexe Lego Technic modellen. Naast deze ervaring kregen we hulp van Linda met haar dochters. Ook Bas met zijn zoon en Koen met zijn blinde enthousiasme hielpen mee. Vooral de toevoeging van de kids (4 tot 8 jaar) creëerde een win-win-win situatie. Het hield ons scherp om precies te werken en duidelijk uit te leggen. Het hielp hiermee onze mentor-skills te verbeteren. De kids kregen daarbij veel en gedegen ervaring in de bouw van een model dat veel complexer is dan voor hun leeftijd bestemd is.
De evenementen die First8 elk jaar organiseert bieden diezelfde win-win situaties. Devoxx4kids is twee keer per jaar ons moment om ons mentorschap flink op de proef te stellen. We geven dan verschillende workshops programmeren aan kinderen van 9 tot 14 jaar. Daarbij beproeven zij ons om eenvoudig en duidelijk de aspecten van programmeren uit te leggen. En wij kunnen vanuit ons vakgebied de passie voor techniek overbrengen.
In onze projecten kiezen we ook voor een mix van ervaren mensen en leergierige juniors. Hierdoor kunnen we onze klanten bedienen met kwaliteit en lage kosten. En ook hierbij krijgt iedereen in zijn rol de voordelen. Kennis en ervaring in techniek voor de juniors, scherp blijven, duidelijk uitleggen en ervaring in goed mentorschap voor de ervaren mensen. Ook bij First8 zoeken we high potentials die zich vast willen bijten in stevige uitdagingen om te groeien, natuurlijk begeleid door Seniors. En we bieden dan ook een stevige uitdaging, zonder dat je bang hoeft te zijn voor falen. Bovendien stimuleren we opleidingen met een stevig opleidingsbudget. En we willen dat iedereen daar ook gebruik van maakt. 

 

 

Reden 4: Open standaarden

Pagina van het patent genaamd "Lego Toy building block" met dwarsdoorsnede van Lego bricks en het "Tube"-design

Sinds 1989 is het patent op het Lego bouwsysteem vervallen. Lego is dus al decennia lang een open standaard. Concurrerende systemen kunnen hun eigen, goedkopere versie van de alom bekende blokjes maken. Die kunnen dan gecombineerd worden met de originele Lego blokjes. Toch is de enige echte Lego nog steeds zeer populair en sinds 2016 zelfs de grootste speelgoedfabrikant ter wereld. Maar hoe kan dit succes verklaard worden? En waarom zien we dan maar weinig Lego concurrenten op de markt?
Bij First8 werken we ook met open standaarden. Dat doen we om makkelijk systemen op elkaar aan te kunnen sluiten. Het geeft onze klanten bovendien de mogelijkheid om probleemloos met andere partijen in zee te gaan. Wij houden niet van vendor lock-in, dus we doen daar zelf ook niet aan mee. We vertrouwen op de kennis en kwaliteit van onze mensen. En we zien dat ook terug in de tevredenheid van onze klanten. Vertrouwen geven en kwaliteit leveren is dan ook veel beter en duurzamer om een relatie op te bouwen en te behouden met onze klanten.
Voor Lego geldt hetzelfde. Het systeem is compatibel met andere bouwblokjes. Als Lego-bouwers weten we dat we niet vastzitten aan het Lego merk. Bovenal zijn we zeker van de perfecte kwaliteit van de originele Lego-blokjes en kiezen we telkens opnieuw voor kwaliteit boven een lagere prijs.

 

Reden 5: Vier je succes

Arjan en Bram houden trots de afgebouwde Millennium Falcon vast

Hard werken moet beloond worden. Daar doe je het voor. Na oplevering hebben we getoost op het succes. En het bouwwerk staat voorlopig als trofee op ons kantoor. Bovendien kunnen we nagenieten van de timelapse die we tijdens de bouw hebben gemaakt. Zo ook in ons normale werk. We hebben elke zomer een uitje, met kerst een diner en in de herfst en lente een borrel. 

Plezier

Alles gaat beter als je er plezier in hebt, zeker als het om complexe uitdagingen gaat. Dit project was ook een behoorlijke uitdaging. Natuurlijk is Lego bouwen al plezier op zich. Ook de nieuwe techniek die wij toepassen in onze projecten zorgt op dezelfde manier voor plezier. We zorgen ervoor dat we leuk, afwisselend en uitdagend werk hebben. Saai en monotoon werk, daar hebben we niks mee. Ook buiten ons werk zorgen we voor plezier. Naast onze uitjes en borrels hebben we elke 2 maanden een bordspellen-avond op ons kantoor. De medewerkers zorgen voor de spellen, onze werkgever zorgt voor de ruimte en hapjes en drankjes.

 

Conclusie

Lego Millennium Falcon ter display op kantoorZoek je goede ICT-ers om een uitdagende klus te klaren? Of ben je zelf Java, Open Source freak en op zoek naar een nieuw uitdaging? In beide gevallen is First8 de beste keus. Wij gaan voor duurzame relaties met onze partners en medewerkers. We zoeken de mooiste uitdagingen. En we gaan voor de beste oplossingen met Open Source techniek en open standaarden. Dat zorgt ervoor dat wij al bijna twee decennia lang een gezonde IT-club runnen. Dat doen we met mensen die hart voor de zaak, voor techniek en voor collega’s hebben. Maar vooral met mensen die ook plezier hebben. En dat zorgt ervoor dat we keer op keer genoeg reden hebben om er een feest van te maken.

Je kunt de Millennium Falcon komen bewonderen bij First8 in Nijmegen met een kop koffie en ons vertellen over jouw passie. Bel Daniëlle Graat of Linda Elbersen (024-3483570 of 06-51784368) voor een afspraak.

 

Bekijk de video:

Bouw-team

Arjan Lamers   Bram Patelski
Bouwt veel Lego Technic. Arjan heeft ruime ervaring met complexe en uitdagende technische modellen, zoals de 42055 Bucket Wheel Excavator. Voor modellen met pneumatische en gemotoriseerde onderdelen draait Arjan zijn hand niet om. Arjan is architect en samen met Bas zijn zij oprichters van First8.
 
  Bouwt voornamelijk Star Wars UCS modellen. Bram heeft bijna alle Star Wars UCS modellen minstens één keer gebouwd en de uitdagende modellen vaker. Door de overeenkomsten met de 10179 UCS Millennium Falcon kon hij zijn ervaring met dat model goed gebruiken bij de bouw van de nieuwe 75192. Bram heeft net een uitdagend Grails project afgerond en heeft ruime ervaring met GitFlow. Hij heeft bovendien brede kennis op privacy en security gebied.
     
Bas Passon (en zoon)   Koen Aben
Bouwt voornamelijk Lego met zijn zoontje, zij het wat eenvoudigere modellen. Bas heeft samen met Arjan in 1999 ons bedrijf opgericht en werkt nog steeds met passie bij First8. Die passie komt met name naar voren als hij Arjan bestookt met kritische vragen over diens keuzes en op die manier zijn partner in crime dwingt om de allerbeste oplossing te kiezen, maar dat ook nog eens moet kunnen uitleggen aan EN overtuigen van zijn luis in de pels.   Bouwt met Lego om Scrum beter uit te leggen. Naast Scrum ook een echte Open Source fanaat. Koen wil graag weten hoe iets werkt en zoekt dat uit tot op het diepste niveau. Hij is oprichter van MADSpace, de hackerspace van Eindhoven, en organiseert graag meetups op gebied van privacy en security.
     
Laura Nij Bijvank   Linda Elbersen (en dochters)
Bouwt veel modular gebouwen, naar eigen inzicht en met haar eigen Lego-collectie. Ze koopt liever losse Lego partijen als complete modellen in doos. Daardoor heeft ze veel ervaring met allerhande bouw-technieken en weet ze goed te improviseren. Ze weet bovendien hoe je overzichtelijk kunt werken met grote hoeveelheden steentjes. Laura is een ervaren Java developer en werkt momenteel aan REST-API’s die JSON data leveren aan meerdere Java en Grails front-end applicaties.   Bouwt met haar dochters kleine en middelgrote Lego modellen. Ze weet haar dochters goed aan te sturen en te stimuleren om uitdaging aan te gaan, ook als het enthousiasme wegvalt. Linda is marketing-guru bij First8. Samen met First8 developers bedenkt ze de meest waanzinnige marketing-stunts en maakt ze dan ook werkelijkheid. Samen met Bram heeft ze dit project uitgedacht en gefaciliteerd, zodat jij nu kunt genieten van een gave timelapse video.
     

Het bericht 5 overeenkomsten tussen Lego en First8 verscheen eerst op First8 Java Consultancy.

]]>
Functional Java by Example | Part 2 – Tell a Story https://technology.first8.nl/functional-java-by-example-part-2-tell-a-story/ Fri, 24 Nov 2017 14:16:54 +0000 https://technology.first8.nl/?p=5984 This is part 2 of the series called “Functional Java by Example”. The example I’m evolving in each part of the series is some kind of “feed handler” which processes documents. In previous part I started with some original code and applied some refactorings to describe “what” instead of “how”. In order to help the code going forward, we need … Lees verder Functional Java by Example | Part 2 – Tell a Story

Het bericht Functional Java by Example | Part 2 – Tell a Story verscheen eerst op First8 Java Consultancy.

]]>
This is part 2 of the series called “Functional Java by Example”.

The example I’m evolving in each part of the series is some kind of “feed handler” which processes documents. In previous part I started with some original code and applied some refactorings to describe “what” instead of “how”.

In order to help the code going forward, we need to tell a story first. That’s where this part comes in.

If you came here for the first time, it’s best to start reading from the beginning. It helps to understand where we started and how we moved forward throughout the series.

These are all the parts:

I will update the links as each article is published. If you are reading this article through content syndication please check the original articles on my blog.

Each time also the code is pushed to this GitHub project.

As a reference, we now have the following code as a starting point:

class FeedHandler {

  Webservice webservice
  DocumentDb documentDb

  void handle(List<Doc> changes) {

    changes
      .findAll { doc -> doc.type == 'important' }
      .each { doc ->

      try {
        def resource = webservice.create(doc)
        doc.apiId = resource.id
        doc.status = 'processed'
      } catch (e) {
        doc.status = 'failed'
        doc.error = e.message
      }
      documentDb.update(doc)
    }
  }
}

Read out aloud

When I first started using Spock as a testing framework, since it came by default with Grails many years ago, I was impressed (and still am) by its many features and ease of use.

SpockFramework

You know what Mocks, Stubs and Spies are, right? Mockito has them, Powermock has them and basically every other serious (unit) testing framework. The concept of a Mock isn’t hard to grasp (you can read all about it here), but Spock has a special way of describing the (expected) interactions with its mocks.

There a great chapter about “Interaction Based Testing” which explains how to write down these interactions with a code sample.

The “Mocking” sub-chapter starts with:

Mocking is the act of describing (mandatory) interactions between the object under specification and its collaborators. Here is an example:

def "should send messages to all subscribers"() {
  when:
  publisher.send("hello")

  then:
  1 * subscriber.receive("hello")
  1 * subscriber2.receive("hello")
}

If you’re not familiar with Spock, with Groovy, or merely with above style of writing, don’t worry!

The author(s) of above Spock documentation too recognized not everyone immediately would understand what’s happening here.

They help with some advice and continue the documentation:

Read out aloud: “When the publisher sends a ‘hello’ message, then both subscribers should receive that message exactly once.”

The emphasis on “Read out aloud” is mine, because I think it’s pretty important. Not going into more Spock details here, but the advice itself I try to take to heart in my day-to-day coding.

  • When I write a piece of code, can I read it out aloud?
  • When someone else reads my code, can he/she read it out aloud?

“Aloud” here has nothing to do with volume 🙂 but with the fact that you can describe “what’s happening here” in a concise and understandable way. This makes it easy to reason about code.

High-level vs low-level

Consider the following scenario:

After driving for hours around in an unknown city to find the Theatre, after your car navigation has broken down, you finally decided to stop and ask for directions.

You stop near a pedestrian.

green-map-pin-flag-icon-655

You:

Sir, would you happen to know how to get to the Theater from here?

Pedestrian:

  1. Sure, that’s pretty easy. Here we go:
  2. Check your windows to make sure you have good visibility. Check mirrors to be sure they are aligned properly, giving you a proper view of the road.
  3. Adjust the seat so that your feet comfortably reach both pedals.
  4. Close the window.
  5. Reset the turn signal.
  6. Disengage your parking brake before you start driving.
  7. Ah, I see that you have an automatic. Please put the gear stick in “Drive”.
  8. Slowly, take your foot of the brake and monitor your dashboard gauges.
  9. Straight on, increase speed, monitoring your speedometer, keeping the RPM around 2000
  10. After around 120 yards, indicate with your blinkers for at least two seconds before beginning to switch to the left lane.
  11. Slowly move the car into the other lane. Turn your hands on the steering wheel ever so slightly in order to change lanes. It only takes a very slight movement of the wheel; as most modern cars are fitted with power steering. It should take anywhere from one to three seconds for you to change lanes. Any less and you’re doing it too quickly; any more and you’re doing it too slowly.
  12. Another X steps…
  13. Good luck!

Or, consider an alternative universe where the conversation would go like this:

You:

Sir, would you happen to know how to get to the Theater from here?

Pedestrian:

  1. Sure, that’s pretty easy. Here we go:
  2. Take a left turn and cross the bridge. It’s on your right.
  3. Good luck!

The last scenario is a breeze: clear directions what to do & where to go!

The first scenario, however, is ridden by details — with low-level specifics about driving the car itself — and even while we would not hope to get directions like that in real-life we still write software like that.

Tell me something on the right level. If I need specifics I’ll ask for it.

(BTW wikihow.com: How to Drive a Car kindly donated some of above instructions. If you actually need to learn to drive, it has a ton o’ resources!)

Telling something on the right level, means not only using properly named classes and methods, but also using the right kind of abstractions in them.

Let’s take a look again at our code:

class FeedHandler {

  Webservice webservice
  DocumentDb documentDb

  void handle(List<Doc> changes) {

    changes
      .findAll { doc -> doc.type == 'important' }
      .each { doc ->

      try {
        def resource = webservice.create(doc)
        doc.apiId = resource.id
        doc.status = 'processed'
      } catch (e) {
        doc.status = 'failed'
        doc.error = e.message
      }
      documentDb.update(doc)
    }
  }
}

The story

How can we combine “read out aloud” and “high-level vs low-level” in our code?

Ted-Vinke-Tell-a-Story

What does our single handle method currently read like?

  1. Find all documents where the type-property equals the string "important".
  2. Call create on webservice with the document, which returns a resource.
  3. If we have a resource, assign the resource’s id to the documents apiId property.
  4. Set the status property of the document to the string "processed".
  5. If an exception occurred, set the status property of the document to the string "failed". Set the status property of the document to the message from the exception.
  6. Finally, call update on documentDb with the document.

Basically this is just repeating the code statements!

What story I’d like to tell instead, is the following:

  1. Process “important” documents by “creating a resource” through a webservice.
  2. Every time when this succeeds, associate both together and “mark the document as processed”, else mark it as “failed”.

Reads pretty well, don’t you think?

We can actually make this happen by using several “Extract method” refactorings in our IDE and choosing some good names for the extracted methods.

The double-quoted phrases in above story are the important bits I want to see at the high-level.

“important”

Why do I care what attribute is used of a document to determine it’s importance? Now it’s the string "important" which indicates “hey, I’m important!” but what if conditionals become more complex?

Extract doc.type == 'important' to its own method, called isImportant.

changes
    .findAll { doc -> isImportant(doc) }
    // ...

  private boolean isImportant(doc) {
    doc.type == 'important'
  }

“creating a resource”

Why do I care here how to invoke what method in a webservice? I just want to create a resource.

Extract all dealings with the webservice to it’s own method, called createResource.

def resource = createResource(doc)
  // ...

  private Resource createResource(doc) {
    webservice.create(doc)
  }

“update to processed”

Extract the details of associating resource/document/setting a status to its own method, called updateToProcessed.

updateToProcessed(doc, resource)
  // ...

  private void updateToProcessed(doc, resource) {
    doc.apiId = resource.id
    doc.status = 'processed'
  }

“update to failed”

Don’t care about the details. Extract to updateToFailed.

updateToFailed(doc, e)
  // ...

  private void updateToFailed(doc, e) {
    doc.status = 'failed'
    doc.error = e.message
  }

Seems that we’re left with documentDb.update(doc) at the end.

This is part of the storing of a processed/failed document in the database and I already described that on the highest level.

I put it in each of the just created updateTo* methods – a lower level.

private void updateToProcessed(doc, resource) {
    doc.apiId = resource.id
    doc.status = 'processed'
    documentDb.update(doc)
  }

  private void updateToFailed(doc, e) {
    doc.status = 'failed'
    doc.error = e.message
    documentDb.update(doc)
  }

So, after extracting the details out, what’s changed?

void handle(List<Doc> changes) {

    changes
      .findAll { doc -> isImportant(doc) }
      .each { doc ->

      try {
        def resource = createResource(doc)
        updateToProcessed(doc, resource)
      } catch (e) {
        updateToFailed(doc, e)
      }
    }
  }

Any human — e.g. co-worker, your future self — who would read this one out “aloud”, would understand what’s going from 30,000 ft.

If you need the details of any of these steps, just drill down into the method.

Being able to write things declarative (previous part of this series) and telling a story on the right level (this part) will also help make future changes more easily in part 3 and beyond.

This is it for now 🙂

For reference, here’s the full version of the refactored code.

class FeedHandler {

  Webservice webservice
  DocumentDb documentDb

  void handle(List<Doc> changes) {

    changes
      .findAll { doc -> isImportant(doc) }
      .each { doc ->

      try {
        def resource = createResource(doc)
        updateToProcessed(doc, resource)
      } catch (e) {
        updateToFailed(doc, e)
      }
    }
  }

  private Resource createResource(doc) {
    webservice.create(doc)
  }

  private boolean isImportant(doc) {
    doc.type == 'important'
  }

  private void updateToProcessed(doc, resource) {
    doc.apiId = resource.id
    doc.status = 'processed'
    documentDb.update(doc)
  }

  private void updateToFailed(doc, e) {
    doc.status = 'failed'
    doc.error = e.message
    documentDb.update(doc)
  }

}

This is a cross-post from my personal blog. Follow me on @tvinke if you like what you’re reading or subscribe to my blog on https://tedvinke.wordpress.com.

Het bericht Functional Java by Example | Part 2 – Tell a Story verscheen eerst op First8 Java Consultancy.

]]>
Functional Java by Example | Part 1 – From Imperative to Declarative https://technology.first8.nl/functional-java-by-example-part-1-from-imperative-to-declarative/ Tue, 07 Nov 2017 00:17:55 +0000 https://technology.first8.nl/?p=5962 Functional Programming (FP) is about avoiding reassigning variables, avoiding mutable data structures, avoiding state and favoring functions all-the-way. What can we learn from FP if we would apply functional techniques to our everyday Java code? In this series called “Functional Java by Example” I will refactor in 8 installments an existing piece of code to see if I can reach … Lees verder Functional Java by Example | Part 1 – From Imperative to Declarative

Het bericht Functional Java by Example | Part 1 – From Imperative to Declarative verscheen eerst op First8 Java Consultancy.

]]>
Functional Programming (FP) is about avoiding reassigning variables, avoiding mutable data structures, avoiding state and favoring functions all-the-way. What can we learn from FP if we would apply functional techniques to our everyday Java code?

In this series called “Functional Java by Example” I will refactor in 8 installments an existing piece of code to see if I can reach Functional Nirvana in Java.

I don’t have much experience in a “real” functional language such as Haskell or F#, but I hope to demonstrate in each article by example what it means to apply some of these practices to your every day Java code.

Hopefully at the end you’ve gained some insight and know to pick some techniques which would benefit your own codebase.

These are all the parts:

I will update the links as each article is published. If you are reading this article through content syndication please check the original articles on my blog.

Each time also the code is pushed to this GitHub project.

Disclaimer: code is written in Apache Groovy, primarily for conciseness, so I don’t have to type stuff (you know: typing) where it doesn’t matter for the examples. Secondary, this language Just Makes Me Happy.

Why should you care about Functional Programming (FP)?

If you’re not doing Haskell, F# or Scala on a hip real-time, streaming data event processing framework you might as well pack your bags. Even the JavaScript guys are spinning functions around your methods these days — and that language has been around for some time already.

There are a lot of articles and video’s out there which make you believe that if you don’t hop on the Functional bandwagon these days, you’re left behind with your old OOP-contraptions and frankly, are obsolete within a couple of years.

Well, I’m here to tell you that’s not entirely true, but FP does have some premises, such as readability, testability and maintainability, values which we also strive to achieve in our (enterprise) Java code right?

As you’re reading this, for years you might already have the same outspoken opinion about FP being a step forwards or backwards or anno 2017-2018 you are just open for new ideas 🙂

You can level up your skills in every language by learning FP.

Determine for yourself what you can learn from it and how your own programming can benefit from it.

If you’re up to the task, let’s start this series with…

Some existing code

A word about example code: It’s pretty tricky to come up with contrived examples for blogs like these: it should be easy enough to appeal to a broad audience, simple enough to be understood without too much context, but still be interesting enough to result in desired learning effects.

Moving forward, each installment in this series will build on the previous one. Below is the code we’re going to take as a starting point.

So, put on your glasses and see if you’re familiar with coding-style below.

class FeedHandler {

  Webservice webservice
  DocumentDb documentDb

  void handle(List<Doc> changes) {

    for (int i = 0; i < changes.size(); i++) {
      def doc = changes[i]
      if (doc.type == 'important') {

        try {
          def resource = webservice.create(doc)
          doc.apiId = resource.id
          doc.status = 'processed'
        } catch (e) {
          doc.status = 'failed'
          doc.error = e.message
        }
        documentDb.update(doc)
      }
    }
  }
}

  • It’s some sort of FeedHandler.
  • It has two properties, some Webservice class and a DocumentDb class.
  • There’s a handle method which does something with a list of Doc objects. Documents?

Try to figure out what’s going on here 🙂

..

..

..

Done?

Reading stuff like this can make you feel like a human parser sometimes.

Scanning the class name (FeedHandler?) and the one method (void handle) can give you, next to some eye sore, a “feel” for the purpose of everything.

However, figuring out what exactly gets “handled” inside the handle method is much harder.

  • There’s a for-loop there — but what’s exactly being iterated? How many times?
  • This variable webservice is called, returning something called resource.
  • If webservice returns successfully, the doc (a document?) being iterated over is updated with a status.
  • Seems webservice can also throw an Exception, which is caught and the document is updated with another status.
  • Ultimately, the document is “updated” by this documentDb instance. Looks like a database.
  • Oh wait, this happens only for the “important” docs — a doc.type is checked first before doing all above stuff.

Perhaps, you have heard of the phrase:

Code is read more than it is written.

Check out this piece of beauty:

for (int i = 0; i < changes.size(); i++) {

Above code is written in an imperative style, which means that the concrete statements — which manipulate state and behaviour — are written out explicitly.

  • Initialize an int i with zero
  • Loop while int i is less then the size of the changes list
  • Increment int i with 1 each iteration

In this style of imperative (procedural) coding (which most of the mainstream languages, including object-oriented programming (OOP) languages, such as Java, C++, C#, were designed to primarily support) a developer writes the exact statements a computer needs to perform to accomplish a certain task.

A few signals of very imperative (procedural) code:

  1. Focus on how to perform the task
  2. State changes and order of execution is important
  3. Many loops and conditionals

The code clearly focuses on the “How” — which makes the “What” hard to determine.

Focus on the What

Our first step, as the title of this article already have away, is to move away from the imperative style of coding and refactor to a more declarative style — of which FP is a form.

The loop is bugging me the most.

Here’s the new version of the code.

class FeedHandler {

  Webservice webservice
  DocumentDb documentDb

  void handle(List<Doc> changes) {

    // for (int i = 0; i < changes.size(); i++) {
    //    def doc = changes[i]
    changes
      .findAll { doc -> doc.type == 'important' }
      .each { doc ->

      try {
        def resource = webservice.create(doc)
        doc.apiId = resource.id
        doc.status = 'processed'
      } catch (e) {
        doc.status = 'failed'
        doc.error = e.message
      }
      documentDb.update(doc)
    }
  }
}

What’s changed?

  • The if (doc.type == 'important') part has been replaced with a findAll { doc -> doc.type == 'important' } again on the document collection itself — meaning “find all documents which are important and return a new collection with only those important documents”
  • The imperative for-loop (with the intermediate i variable) has been replaced by the declarative each method on the documents collection itself — meaning “execute the piece of code for each doc in the list and I don’t care how you do it” 🙂

Don’t worry about each and findAll: these methods are added by Groovy, which I use happily together with Java in the same code base, to any Collection, e.g. Set, List, Map. Vanilla Java 8 has equivalent mechanisms, such as forEach to iterate a collection more declaratively.

What leads to readable software is:

Describe “What” and not “How”.

I can easily see what’s going on if I write my code in a more functional style, which saves me time (because yes, I do read code 90% of the time instead of writing it) and writing it like this is less error-prone, because less lines gives less opportunity for bugs to hide.

This is it for now 🙂

In part 2, we will tell a story properly, paving the way for more functional programming, such as “Either” or “Try” even later in the series.

This is a cross-post from my personal blog. Follow me on @tvinke if you like what you’re reading or subscribe to my blog on https://tedvinke.wordpress.com.

Het bericht Functional Java by Example | Part 1 – From Imperative to Declarative verscheen eerst op First8 Java Consultancy.

]]>
Why is Spring’s Health Down, Down, Up, Up, Up and Down again? https://technology.first8.nl/why-is-springs-health-down-down-up-up-up-and-down-again/ Wed, 25 Oct 2017 07:45:39 +0000 https://technology.first8.nl/?p=5957 Why Our new JavaScript client application regularly calls the /health endpoint of our Grails backend to determine on- of offline state. Things started to become “funny” with it. This endpoint we get for free, since Grails is based on Spring Boot, which comes with a sub-project called Spring Boot Actuator. This gives us a a bunch of endpoints which allows … Lees verder Why is Spring’s Health Down, Down, Up, Up, Up and Down again?

Het bericht Why is Spring’s Health Down, Down, Up, Up, Up and Down again? verscheen eerst op First8 Java Consultancy.

]]>
Why

Our new JavaScript client application regularly calls the /health endpoint of our Grails backend to determine on- of offline state. Things started to become “funny” with it.

This endpoint we get for free, since Grails is based on Spring Boot, which comes with a sub-project called Spring Boot Actuator.

This gives us a a bunch of endpoints which allows us to monitor and interact with our application, including /health which returns health information.

So, our JS client checks whether or not it can reach this /health endpoint, executed every few seconds, to determine if the user is on- or offline. Nothing fancy, and we might switch later on to just using the Google homepage or something, but for now this works.

Failing health check

On localhost everything always seems fine, but as soon as I got our Jenkins pipeline finally to deploy the app to to our test servers after each build, and we started veryfying the app there, things became funny.

Usually we had a streak of perfectly good calls.

GET https://tst.example.com/health 200 ()
GET https://tst.example.com/health 200 ()
GET https://tst.example.com/health 200 ()
etc

Other times every few seconds we saw errors accumulating in the Chrome Inspector. Health checks would fail with with a HTTP status code of 503 Service unavailable for a long time.

GET https://tst.example.com/health 503 ()
GET https://tst.example.com/health 503 ()
GET https://tst.example.com/health 503 ()
etc

Then after a while we would get good calls again!

GET https://tst.example.com/health 200 ()
GET https://tst.example.com/health 200 ()
etc

The response of these failed requests just said

{"status":"DOWN"}

This is — by design — not very descriptive.

I certainly did not write any healh indicators myself so why would it be “down”?

Experienced Spring Booters know it will pick up any health indicator on the classpath and comes default with a few. Which ones are actually in use can be a mystery, because by default this endpoint is classified by Spring Boot as “sensitive” — and thus doesn’t expose too much information to the outside world.

I had to make the health check a bit more “chatty” by setting the following setting:

endpoints.health.sensitive: false

Now, calling the endpoint manually revealed the contenders!

{
  "status":"DOWN",
  "diskSpace":{
    "status":"DOWN",
    "total":8579448832,
    "free":20480,
    "threshold":10485760
  },
  "db":{
    "status":"UP",
    "database":"H2",
    "hello":1
  }
}

The general status of “down” is an aggregate result of (in this case: 2) auto-configured health indicators listed explicitly now.

What inmediately came to mind, when I saw this:

  • Why didn’t I remove H2 yet 🙂
  • Hey, disk space is running out on the test server already?!

The H2 database comes as a default dependency in any Grails application, but our app doesn’t use it — not in production and not for testing — so we will definately remove it from the dependencies. That’s a worry less.

With regard to disk space, it’s the good ol’ DiskSpaceHealthIndicator (indeed part of the auto-configured indicators) telling me things are unhealthy.

It has a default threshold of 10485760 bytes or 10 MB — the minimum disk space that should be available.

And…there’s only 20 kb free space? Of 8 gigs in total.

That’s a pretty low number 🙂

In the first 0.7 seconds I didn’t believe the healt indicator, can you imagine?

So I SSH’ed into the test server to check the available disk space with the df utility:

[Ted@server-01t ~]$ df -h
Filesystem             Size  Used Avail Use% Mounted on
/dev/mapper/rhel-root  8.0G  8.0G   20K 100% /
...

Right, at least the health check speaks the truth there: there’s actually only a tiny bit of space left.

I relayed this to my IT collegue which provisioned this machine, to investigate. Seemed that there were already some Java heap dumps from earlier experiments taking up the space — which I was told will be removed ASAP.

Better check the other node too.

[Ted@server-02t ~]$ df -h
Filesystem             Size  Used Avail Use% Mounted on
/dev/mapper/rhel-root  8.0G  5.3G  2.8G  66% /

Enough room there.

Wait a minute? “Other node?” Yes, we have 2 test servers, 01t and 02t.

At that point, I realized: the behaviour I was seeing was because of the loadbalancer forwarding a request to tst.example.com to either server-01t or the other `server-02t’. One of them was low on disk space, which explains that the health indicator of the Grails app on that server says “down” – resulting in a HTTP 503.

When observing these health calls (which requests are continuously made by our JS client) through the Chrome Inspector one small question was left: why do we have a streak of (sometimes 50x) “ups” (200) and then a bunch of “downs” (503) then in a seemingly random order?

The load balancer should keep us “fixed” on that node where a JS client for the first time makes its requests, as we configure our servers like that.

If the loadbalancer would send every request (to tst.example.com) round robin to server 1 or 2, I would expect a more (random) response of e.g. “up”, “down”, “down”, “up”, “down”, “up”, “up”, “down”, “up”.

Well, it seemed that during the window while I was observing this behaviour, the rest of the team was still developing features and…pushing to Git, which Jenkins picks up, which gets deployed to both servers. Because of a redeploy of the app to ech server serially, the loadbalancer “sees” the unavailibility of the application on the one server (with enough disk space: “up”, “up”, “up”, “up”, “up”) for the duration of the deployment and redirects traffic to the other server (with almost no disk space: “down”, “down”, “down”)…

…which gets updated with a new WAR pretty soon after, and requests end up on the other server again (with enough disk space: “up”, “up”, “up”, “up”, “up”).

🙂

Costs again 3 hours out of my life. Including some time noting down this stuff here (but I think that’s worth it) 🙂

Lesson learned

Know your process

Knowing that there’s a loadbalancer and multiple nodes (and how they work) helps. And that your CI server continuously deploys new versions to your environment which is under investigation does not help. But altogether knowing this did help to clarify the observed behaviour.

Learn the “sensible” defaults of your framework.

In case of Grails 3 and Spring Boot, know the stuff which gets “auto-configured” from the classpath, inspect it and make sure it’s going to be what you actually want.

We will get rid of H2 and review the health indicators we actually need, may be disabling the auto-configuration altogether. We cleaned up the Java heap dumps which caused the full disk. We’ve re-confirmed that the Unix team will monitor the OS, including disk space, so that we at least don’t need the DiskSpaceHealthIndicator anymore 🙂

This article has been crossposted from my personal blog.

Het bericht Why is Spring’s Health Down, Down, Up, Up, Up and Down again? verscheen eerst op First8 Java Consultancy.

]]>
Jenkins shared libraries: tested https://technology.first8.nl/jenkins-shared-libraries-tested/ Sun, 15 Oct 2017 08:04:11 +0000 https://technology.first8.nl/?p=5625 Jenkins is a very neat tool to implement a continuous delivery process, mainly due to its flexibility. Sometimes it can be hard though to keep complexity low, and when that happens, (automated) tests become far more important. Jenkins should in fact be running tests that verify the scripts running tests, proving they actually work. Warning: this dog will chase its … Lees verder Jenkins shared libraries: tested

Het bericht Jenkins shared libraries: tested verscheen eerst op First8 Java Consultancy.

]]>
Jenkins is a very neat tool to implement a continuous delivery process, mainly due to its flexibility. Sometimes it can be hard though to keep complexity low, and when that happens, (automated) tests become far more important.

Jenkins should in fact be running tests that verify the scripts running tests, proving they actually work. Warning: this dog will chase its own tail.

 

Jenkins

Jenkins pipeline scripts

Once, not so long ago, the way to go was to manage jobs manually using the GUI. At the moment Cloudbees is proposing pipelines as scripts, either scripted procedurally or declarative.

Declarative pipelines are still in development. Their primary intent is to be suitable to be edited from a simple GUI.

 

Extending Jenkins scripting

In the world of open source, integration is becoming the only major thing a company needs to implement themselves.

Using just a single script however will get you only so far. At a certain point you will want to introduce abstractions.

 

The Jenkins Shared library

Cloudbees recently introduced a way of having more than just a script, but keeping things relatively easy and secure to use.

The ‘shared library’ is a Jenkins concept that allows projects to declaratively import a full repository of classes and scripts.

In short, any shared library will have a few common folders:

/ -- Jenkins will load the complete repository, the following places are important:

/src -- classes, optionally using @Grab to get dependencies
/vars -- scripts, closures, dsl building
/resources -- anything else, stuff

 

It comes with tests

As with all new areas of applying code, once maturity is reached, tests become relevant. Fortunately there is a way to run tests for the pipeline code, be it scripts or (typed) classes.

These tests will lean towards unit tests even though the unit boundaries for pipelines are not that clear. Interaction with external systems is of course not something best covered using unit tests. The following examples are aimed primarily at testing groovy compilation, execution and (emulated) interpretation by Jenkins. When defining clear units (classes), these can become proper targets for unit tests once more.

 

JUnit example

Note that the code is groovy, but it can just be java, just add semi colons 😉 (okay maybe a few anonymous classes etc, but should be possible).

package com.first8

import org.junit.Before
import org.junit.Test

import com.lesfurets.jenkins.unit.BasePipelineTest

class BasicPipelineTest extends BasePipelineTest {
        
    @Before
    void before() {
    super.setUp()
        
    helper.registerAllowedMethod("pwd", []) { "/tmp/testworld/doesnotexist" }
    helper.registerAllowedMethod("stash", [Map.class]) { println "mock stash called." }
    }
    
    @Test
    void happyFlowLoading() throws Exception {
        Script script = loadScript("resources/com/first8/grails3/DefaultJenkinsfile")
        
        printCallStack()
    }
    
}

Spock example

Since the pipeline scripts use groovy, one might as well write tests using groovy.

abstract class JenkinsSpecification extends Specification implements RegressionTest {

    /**
     * Delegate to the junit cps transforming base test
     */
    @Delegate
    BasePipelineTestCPS baseTest

    /**
     * Do the common setup
     */
    def setup() {

        // Set callstacks path for RegressionTest
        callStackPath = 'jenkinsSpec/callstacks/'

        baseTest = new BasePipelineTestCPS()
        baseTest.setUp()
    }
}

class ExampleSpec extends JenkinsSpecification {

    def setup() {
        // like with junit, the helper is available here
    }

    void cleanup() {
        printCallStack()
    }

    def "example spec"() {
        when:

            println "nothing happens in this spock specification, success!"
        
        then:
            assert true
    }

}

 

Project layout

To be able to work with a shared library, a project setup would be convenient, so why not use groovy through gradle? The layout of a Jenkins shared library is not standard for gradle. This means that a little config is needed.

Once the configuration is in place, the project will build quite easily. There are some snags though, which will show in the gradle config file below. The little problems together make the build.gradle file not quite as trivial.

 

 

Gradle config

Check the following build.gradle config, there are hints on possible difficulties in the comments.

// Apply the java plugin to add support for Java
apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'project-report'

targetCompatibility = '1.8'
sourceCompatibility = '1.8'

model {
    components {
        main(JvmLibrarySpec) {
            targetPlatform 'java8'
        }
    }
}

// follow the structure as dictated by Jenkins:
sourceSets {
    main {
        groovy {
            srcDirs = ['src','vars']
        }
        resources {
            srcDirs = ['resources']
        }
    }
    test {
        groovy {
            srcDirs = ['test']
        }
    }
}

// allow for the (pipeline code) Ivy Grab/grape system to do some setup...
String home = System.getProperty("user.home")
task intializeGrapeConfig(type: Copy) {
    doFirst {        
        println "installing grape config in: ${home}/.groovy"
    }    
    from '.'
    include "grapeConfig.xml" // assuming that this file is in the shared library!
    into "${home}/.groovy/"
}
intializeGrapeConfig.group = "build setup"

// In this section you declare where to find the dependencies of your project
repositories {
    // Use 'jcenter' for resolving your dependencies.
    // You can declare any Maven/Ivy/file repository here.
    mavenCentral()
    jcenter()
    maven {
        url "http://repo.jenkins-ci.org/releases/"
    }
}

// In this section you declare the dependencies for your production and test code
dependencies {
    // to be sure @Grab compile time dependency downloading works in scripts, have the goods ready
    compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.9'
    compile group: 'org.apache.ivy', name:'ivy', version:'2.4.0'

    // The production code uses the SLF4J logging API at compile time
    compile 'org.slf4j:slf4j-api:1.7.21'

    // Declare the dependency for your favourite test framework you want to use in your tests.
    // TestNG is also supported by the Gradle Test task. Just change the
    // testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
    // 'test.useTestNG()' to your build script.
    compile 'junit:junit:4.12'
    
    // ======= jenkins pipeline unit testing framework ====== //
    compile group:'com.lesfurets', name:'jenkins-pipeline-unit', version:'1.0'
    
    /**
     * For Spock unit tests (not needed when just using JUnit)
     */
    testCompile 'org.spockframework:spock-core:1.1-groovy-2.4'
    testCompile 'cglib:cglib-nodep:3.2.2'
    testCompile 'org.objenesis:objenesis:1.2'
    testCompile 'org.assertj:assertj-core:3.7.0'

    // ============== base of jenkins =============== //
    // (this list may grow, just try to minimize libs coming in):
    compile('org.jenkins-ci.main:jenkins-core:2.69') {
        transitive = false
    }
    compile('org.kohsuke.stapler:stapler:1.251') {
        transitive = false
    }
    
    // ========= parts of jenkins plugins ============ //
    // might by used in pipeline scripts (note the @jar!):
    compile 'org.jenkins-ci.plugins.workflow:workflow-step-api:2.12@jar'

    // ========= PIPELINE COMPONENT DEPENDENCIES ========//
    // since ivy @Grab is not emulated, we need to include them this way
    compile 'org.jfrog.artifactory.client:artifactory-java-client-services:0.16'
    
}

// make sure groovy compiler has all the dependencies before! starting compiling (@Grab needs ivy for instance)
tasks.withType(GroovyCompile) {
    groovyClasspath += configurations.compile
}

Running the tests

Since gradle can be started using the brilliant gradle wrapper, starting the build from Jenkins is easy.

It’s enough to create a typical ‘multi-branch job’ and have a Jenkinsfile with the following:

sh "./gradlew verify"

Now each push to the shared library repository will start a test run producing early warnings if something is wrong.

 

More info

 

Chasing tails

The recently developed option to test pipeline code is great. As with all testing options though it’s important to keep in mind whether the extra testing effort is worth it.

Having Jenkins run tests on test code that runs tests seems kind of pointless, after all tests will (hopefully) fail both when the production code is broken and when the tests are broken. It turns out that having some trivial unit tests for pipeline code does deliver a lot of value by creating a very short feedback loop. In terms of knowing whether the pipeline actually does the ‘right’ thing is a different matter. Tests that can assert this are far more complicated, true system tests are probably more suitable for that purpose.

 

Some things to think about:

  • what is continuous passing style?
  • what would be a convenient strategy to actually integration-/system-test new pipeline code?

 

Het bericht Jenkins shared libraries: tested verscheen eerst op First8 Java Consultancy.

]]>
Spring WebFlux annotation based https://technology.first8.nl/spring-webflux-annotation-based/ Thu, 05 Oct 2017 14:13:55 +0000 https://technology.first8.nl/?p=5925 The release of Spring Framework version 5 is just around the corner and it contains a lot of new features. One of those is the WebFlux framework. It adds support for reactive web programming on client and server side. Spring WebFlux uses Reactor to make it all possible. The server side supports two programming models: Annotation based, using @Controller annotations. … Lees verder Spring WebFlux annotation based

Het bericht Spring WebFlux annotation based verscheen eerst op First8 Java Consultancy.

]]>
The release of Spring Framework version 5 is just around the corner and it contains a lot of new features. One of those is the WebFlux framework. It adds support for reactive web programming on client and server side. Spring WebFlux uses Reactor to make it all possible. The server side supports two programming models:

  • Annotation based, using @Controller annotations.
  • Functional, using Java 8 lambdas for routing and request handling.

The annotation based model correlates most to what we have been doing for years using Spring MVC. We are able to use all the things we know, but in a reactive manner. On the outside there is little difference. The biggest change is on the inside. The underlying implemenation is based on a reactive implementation of HttpServletRequest and HttpServletResponse. This blog focuses on the annotation based model. A future blog will show the functional model.

Let’s have a look at a a simple application that serves the names of the planets in our solar system, optionally applying a filter, as plain text using a line for each planet. Using curl I would like to see the following:

$ curl http://localhost:8080/planets
Mercury
Venus
Earth
Mars
Jupiter
Saturn
Uranus
Neptune
$ curl http://localhost:8080/planets/s
Venus
Mars
Saturn
Uranus

Not too spectacular, but it will suit the purpose of this blog. A Spring MVC controller doing the job will look something like this:

@RestController
@AllArgsConstructor
public class PlanetController {

    private PlanetRepository planetRepository;

    @RequestMapping(path={"/planets/{substring}","/planets"})
    public String getMatchingPlanets(@PathVariable(name = "substring", required=false) Optional<String> substring) {
        return planetRepository.getPlanets()
                .filter( p -> p.toLowerCase().contains(substring.orElse("").toLowerCase()))
                .reduce("",  (l,r) -> l + r + "\n");
    }
}

Nothing strange here. We have a PlanetRepository that gives us a Stream<String> of planets. A controller with a @RequestMapping to handle the request and produce a suitable response. Switching to Spring WebFlux requires some changes. First change the dependency on Spring MVC to Spring WebFlux. If you are like me and use Spring Boot, you can swap spring-boot-starter-web for spring-boot-starter-webflux. Next we need to update the response type of the request mapping methods to either Flux<String> or Mono<String>. WebFlux requires the request handling methods to return on of these types. The updated controller looks like:

@RestController
@AllArgsConstructor
public class PlanetController {

    private PlanetRepository planetRepository;

    @RequestMapping(path={"/planets/{substring}","/planets"})
    public Flux<String> getMatchingPlanets(@PathVariable(name = "substring", required=false) Optional<String> substring) {
        return Flux.fromStream(planetRepository.getPlanets()
                .filter( p -> p.toLowerCase().contains(substring.orElse("").toLowerCase()))
                .map( p -> p + "\n"));
    }
}

As you can see we now return a response of Flux<String>. We could have used Mono<String> which is used for non collection types, but as we are showing one planet per line, we can treat it as a stream of planets. Therefore a Flux<String> is the better option.

As mentioned before, we are using a PlanetRepository that supplies us with a Stream<String> of planets and to convert it to a Flux<String> type, we use the static factory method Flux.fromStream(Stream<T>). As a flux is quite similar to a stream, we can make our code a bit cleaner by having the PlanetRepository return a Flux<String> instead of a Stream<String>. Our controller will then look like:

@RestController
@AllArgsConstructor
public class PlanetController {

    private PlanetRepository planetRepository;

    @RequestMapping(path={"/planets/{substring}","/planets"})
    public Flux<String> getMatchingPlanets(@PathVariable(name = "substring", required=false) Optional<String> substring) {
        return planetRepository.getPlanets()
                .filter( p -> p.toLowerCase().contains(substring.orElse("").toLowerCase()))
                .map( p -> p + "\n");
    }
}

There you go – a fully reactive web controller. The full source code for the project can be found on github.

This is a cross-post from my personal blog. Follow me on @acuriouscoder if you like what you’re reading or subscribe to my blog on https://acuriouscoder.net.

Het bericht Spring WebFlux annotation based verscheen eerst op First8 Java Consultancy.

]]>
Strangling pipelines https://technology.first8.nl/strangling-pipelines/ Tue, 29 Aug 2017 08:47:40 +0000 https://technology.first8.nl/?p=5811 Strangling pipelines This practical example is about the strangulation pattern, as explained by Martin Fowler here, applied to pipelines.   The situation Right after ditching the old manually managed Jenkins jobs, we were left with ‘simple’ but very lengthy procedural pipeline scripts. These scripts were then duplicated and slightly modified for each type of pipeline (because there’s always need for … Lees verder Strangling pipelines

Het bericht Strangling pipelines verscheen eerst op First8 Java Consultancy.

]]>
Strangling pipelines
Supported by Redhat

This practical example is about the strangulation pattern, as explained by Martin Fowler here, applied to pipelines.

 

The situation

Right after ditching the old manually managed Jenkins jobs, we were left with ‘simple’ but very lengthy procedural pipeline scripts. These scripts were then duplicated and slightly modified for each type of pipeline (because there’s always need for another ‘standard’ way of doing things).

Maintenance meant keeping changes synchronized between the scripts but taking care of subtle but intentional differences. This becomes increasingly difficult as more complexity is added, due to adding features to continuous delivery.

To give an example of the structure, and potential for being lengthy, take a look at the following.

#!/usr/bin/env groovy

/**
 * Read this first: <a href="https://github.com/jenkinsci/pipeline-examples/blob/master/docs/BEST_PRACTICES.md">Best Practices</a><br>
 *
 * Example usage of this script:
 *
 <pre>
 def sharedBuild
 node {
   sharedBuild = load 'DefaultJenkinsfile'
 }

 sharedBuild.buildAny([DEV:[host1]], "/context", ["my.email@mydomain.com"])
 </pre>
 */

def buildAny(Map<String,List<String>> hosts, String context, String recipients) {

    def branchName = env.BRANCH_NAME

    if (branchName in ['trunk']) { // yes svn still lives.
        buildTrunk(hosts, context, recipients)
    } else {
        buildBranch(recipients)
    }
}

/**
 * Trunk builds are built, and then deployed throughout the various DTAP servers.
 * @see #buildAny
 */
def buildTrunk(Map<String,List<String>> hostNameMap, String context, String recipients) {

    def mavenRepoUrl

    node {
        stage("prepare-build") {
            echo "hosts: ${hostNameMap}"
            echo "working dir: ${pwd()}"

            cleanCheckout()
        }

        String war = build()

        stage("validate") {
            mavenRepoUrl = storeSnapshot(war)
        }
    }

    node {
        echo "starting automatic deployments"
        String downloadedWar = downloadAppWar(mavenRepoUrl)

        if (hostNameMap.DEV) { // not all pipelines have the 'D' in DTAP
            stage("deploy to DEV") {
                deploy(downloadedWar, hostNameMap.DEV, context)
            }
        }

    }

    askForAccApproval(recipients)

    node {
        stage("deploy to ACC") {
            String downloadedWar = downloadAppWar(mavenRepoUrl)

            deploy(downloadedWar, hostNameMap.ACC, context)

            storeRelease(downloadedWar)
        }

    }

    // Not shown here: when things fail, mail recipients etc.
}

/**
 * By default, branches for now only create an artifact, no deployments are done.
 * @see #buildAny
 */
def buildBranch(String recipients) {

    node {
        stage("prepare-build") {
            echo "working dir: ${pwd()}"
            checkout scm
        }

        String war = build()
        stage("validate") {
            checkWar(war)
        }
    }

    // Not shown here: when things fail, mail recipients etc.
}

def askForAccApproval(String recipients) { /* ... */}
def deploy(String warFile, List<String> hosts, String context) { /* ... */}
def cleanCheckout()  { /* ... */ }
def build()  { /* ... */ }
def storeSnapshot(String warFile) { /* ... */ }
def storeRelease(String warFile) { /* ... */ }
def downloadAppWar(String repoUrl) { /* ... */ }
def checkWar(String warFile) { /* ... */ }

// make script reusable:
return this

 

The target

An elegant library of components to help setup a pipeline made of a simple flow that calls out to these well defined, interchangeable units. These components are documented, have types and do proper error handling.

For brevity, this considers only the trunk part, building branches is left out.

// let's say these are 'components', a.k.a. 'little machines'
def context
def vcs
def builder
def repo
def container
def buildPromoter


def warUrl
node {
    stage("prepare-build") {
        context.printEnvironment(this)

        vcs.cleanCheckout()
    }

    String war

    stage("build") {

        war = builder.build(this)
    }

    stage("validate") {
        warUrl = repo.storeSnapshot(this, war)
    }
}

node {
    String downloadedWar = repo.download(this, warUrl)

    // this is the project's Jenkinsfile, so commenting out stuff is easy
    stage("deploy to DEV") {
        container.deploy(this, downloadedWar, "dev")
    }

}

buildPromoter.askForAccApproval(recipients)

node {
    stage("deploy to ACC") {
        String downloadedWar = repo.downloadAppWar(this, warUrl)

        container.deploy(this, downloadedWar, "acc")

        repo.storeRelease(this, downloadedWar)
    }

}

You may be able to imagine that switching containers, repositories, VCS system, can be managed a lot easier with this code. Of course there are questions to be answered, like:

  • how does the component code get in the project code base?
  • how do the (correct) components get instantiated?
  • how to do effective configuration for the various components?
  • can we get around limitations like CPS transformation, lack of dependency injection, etc?

 

On how to get there

Getting from the old situation to the new is actually, by itself, not that hard. The thing though is that the world does not stop while doing the refactor work. New teams, new features, bugfixes, production deployments, etc happen during the refactoring.

Those changes need to happen across all pipelines. To make sure there’s just one relevant codebase, we apply ‘strangulation’.

 At a minimum, the following steps are involved:

  1. Create a shared library repository (and configure it in Jenkins)
  2. Move old scripts into this library, as resources (just text file)
  3. Change a project’s  ‘Jenkinsfile‘ files to load the shared library
  4. Load the old script as resource
  5. Setup a ‘context‘ that can be accessed in a static way, like a poor mans DI solution
  6. Create new components (use this pattern)
  7. Replace parts of the script with calls to new components retrieved using the context (use branches!)
  8. Clean out remaining script code
  9. Replace the loading script with just a few simple statements to components

 

About having a poormans DI solution

Jenkins actually uses Guice internally to offer DI. In scripting code this is not available (at least not by default).

At some point it might become available in some form, so it would be nice to not have to do a complete rewrite once that moment comes.

Having a static way to access some kind of context is the simplest way to always have access to the needed dependencies. Only one hardwired link is needed to the context mechanism, all logic behind it is interchangeable. Statics are bad for tests, so it becomes important to be strict about having just this one instance, as said a poormans solution.

However, when there’s context:

  • You can control whether to use new components by leveraging the ‘context‘.
  • One of the components may be a central configuration for instance
  • There may be multiple implementations of a single component contract (interface)
  • Components may depend on each other while the script does not need to know

Getting and creating components can be made dynamic to allow switching ‘branches’ when doing branch by abstraction. The context is the main abstraction.

class Context {

  private static Context INSTANCE

  static Context get() {
    if (INSTANCE == null) {
      INSTANCE = new Context()
    }
    return INSTANCE
  }

  // get components from the instance (not static)
  MyBean getMyBean() {
    new SomeExtendedMyBean()
  }

  // for use with tests
  static void stub(Context fake) {
    Context.INSTANCE = fake
  }

}

// Example call:
Context.get().getMyBean().doSomeThing()

 

About Jenkins’ groovy limitations

CPS transformation (continuation passing style) imposes restrictions on pipeline code.

Read the following, including the warnings:

 

Wrapping things up

By using branch by abstraction, replacing old parts of any code becomes a series of manageable changes while keeping things running.

This mechanism translates quite well to pipelines.

Now when you get to having an awesome shared library, you might actually want to build, validate and test it (and let Jenkins do the heavy lifting)! Spoilers here. More on this subject soon!

 

 

Het bericht Strangling pipelines verscheen eerst op First8 Java Consultancy.

]]>