GlassFish 3.1

Idag släpptes applikationsservern GlassFish 3.1. Det mycket i den releasen som är värt att titta noga på och även om applikationsservrar ända sedan J2EE har haft svårt att få acceptans i systemutvecklarkretsar. Val av applikationsserver är ett strategiskt val, det är viktigt att man som företag “satsar på rätt häst”, efter som det är stor sannolikhet att man får leva med valet i flera år framöver.
GlassFish är referens implementation till Java EE specifikationen. Det betyder konkret att GlassFish alltid kommer ligga i framkant när Java och Java EE standarden utvecklas. När detta skrivs är vi på Java EE 6, en standard som innehåller väldigt mycket som höjer abstraktionsnivån på hur avancerade system utvecklas GlassFish är i dagsläget enda applikationsservern som implementerar hela Java EE 6 standarden. Standarden kräver att applikationsservern sköter det mesta av det som man för bara några åt sedan var tvungen att hacka mängder med XML för att få till. Man har tagit best practices från ramverk som Spring, Hibernate, Google Guice och Seam för att bygga upp en programmeringsmodell som bevisligen fungerar. GlassFish är således verkligen en plattform för att programmera på ett effektivt sätt.
GlassFish är ombyggd från 2.x versionen till att vara helt modulär och baserad på OSGi plattformen Apache Felix. Det betyder konkret att man har en plattform som är modulär och där man bara behöver köra de moduler som verkligen behövs. Det betyder dessutom att det är enkelt att vidareutveckla plattformen med OSGi boundles som deployas på Felix. GlassFish kan till och med interagera med OSGi boundles via injections i Java EE applikationer (sk. Hybridapplikationer).
GlassFish har i och med 3.1 åter fått stöd för klusting och central administration. Det är goda nyheter för företag med stora data centers som behöver en bra överblick över installationer och miljö och en hög tillgänglighet. Kort sagt, GlassFish tillför så mycket värde att det kanske, nästan överväger debaclet med Oracle -> Sun.

Att utveckla för driftbarhet

Ordet systemutveckling syftar till att handla om mer än programmering av affärslogik. Det syftar till att utveckla systemet som ska skapa affärsvärde. Har man väl vridit hjärnan till ett sådant tankesätt, är det lätt att ta till sig att saker som hur loggar skrivs ut potentiellt kan vara en väldigt viktig del av programmeringen. Den stackars jourpersonen som kl 03 ska försöka reda ut varför företaget plötsligt blöder pengar, vill nog gärna att Nagios, Hyperiq eller vad man nu använder; triggas på rätt sätt. Övervakningssystem som dem tittar ofta på loggar för att rapportera fel. Det betyder att loggar potentiellt kan vara väldigt viktiga == bör testas! Nedan visas ett exempel på hur vi gjorde ett enkelt test för att verifiera själva logutskriften hos en kund.
Vi tänker oss att vi har en service “MyService” som använder sig av en wrapper “RemoteProviderWrapper” för att anropa en extern tjänst av något slag.

package se.diabol.test.logunit
import org.apache.log4j.Logger
class MyService {
  private Logger log = Logger.getLogger(MyService.class)
  RemoteProviderWrapper remoteProviderWrapper = new RemoteProviderWrapper()
  void shakyServiceMethod(String parameter){
    try{
      remoteProviderWrapper.callRemoteProvider(parameter)
    }catch(RemoteProviderCommunicationException e){
      log.error "Communication failure with external provider: ${e.message}"
      // Do cleanup, rollback transactions, report to the user..
    }
  }
}

Om man tittar vi på vad som händer i själva catch blocket (jag är fullt medveten om att det inte är optimalt att lösta exception hantering på detta sätt, men det är bara ett exempel). Det skrivs ut ett felmeddelande som talar om att det inte går att kommunicera med den externa tjänsten. Denna loggutskrift är guld värd för någon som, utan programmerardjup kunskap om systemet, ska förstå vad som är roten till problemet. Hela systemet kan ju just nu stå och kräkas ur sig stackutskrifter från exceptions. Antagligen finns det en trigger i övervakningssystemet på denna logutskrift. Alltså bör vi testa att detta logmeddelande verkligen kommer ut som en del i vårt testramverk. Jag har skapat en log4j appender som sväljer alla meddelenaden för att senare i en testkod kunna hämta ut dem och verifiera vad de innehåller:

package se.diabol.test.logunit
import org.apache.log4j.AppenderSkeleton
import org.apache.log4j.spi.LoggingEvent
class UnitTestAppender extends AppenderSkeleton { 
  private List messages = []  
  protected void append(LoggingEvent event) {    
    messages += event.renderedMessage  
  }  
  void close() {}  
  boolean requiresLayout() {return false}  
  String[] getMessages() {
    return messages as String[]
  }
  void clear() {
    messages = []
  }
}

Ok, nu har vi en appender, då är det bara att tala om för log4j att den ska användas (sätter det på root loggern här)

log4j.rootCategory=DEBUG, junittestappender
log4j.appender.junittestappender=se.diabol.test.logunit.UnitTestAppender

Därefter kan man skriva ett unittest som använder appendern för att verifiera loggningen som sker i koden.

package se.diabol.test.logunit

import org.junit.Test
import groovy.mock.interceptor.*
import org.apache.log4j.LogManager

class MyServiceTest {
    @Test
    void makeFailedCallToRemoteProvider(){
        def appender = LogManager.rootLogger.getAppender("junittestappender")
        appender.clear()

        def mock = new MockFor(RemoteProviderWrapper.class)
        mock.demand.callRemoteProvider { throw new RemoteProviderCommunicationException("Communication link down!")}
        mock.use{
            new MyService().shakyServiceMethod("Test parameter")
        }

        assert appender.messages == ["Communication failure with external provider: Communication link down!"]
    }
}

Jag börjar med att hämta ut den appender som log4j har skapad och tömmer eventuella gamla meddelanden “clear()”. Sen använder jag grooys inbyggda mockningsfunktionalitet för att skapa en mockad “RemoteProviderWrapper”. Denna mock kommer skicka ett “RemoteProviderCommunicationException” varje gång den anropas. Innanför “mock.use{” blocket kommer alla försök att skapa en “RemoteProviderWrapper” returnera den mock jag angivit ovanför. Jag behöver alltså inte tala om för MyService att den ska använda den mockade klassen på något annat sätt. Sen anropar jag service metoden “shakyServiceMethod”. När det är gjort, hämtar jag ut allt som lagrats i test appendern och verifierar att en logutskrift verkligen skett.
Vad var nu sysftet med detta? Jo, plötsligt har blicken lyfts lite. Här finns en tanke om att det inte bara är själva koden som är viktigt. Denna kod sitter antagligen i någon modul som ingår i ett system. För modulen och programmeraren är loggningen helt irrelevant. Men för systemet och verksamheten kan det vara fråga om skillnader i timmars felsökning för att hitta en extern tjänst som gått ner, eller se ett korrekt felmeddelande i övervakningssystemet. Det är vad utveckling för driftbarhet handlar om.
Nästa steg är att även involvera nämnt övervakningssystem i systemutvecklingen och börja göra automatiska funktionstester som även omfattar övervakning.

Myten om kvalitetskompromissen

Alla som har hört frasen “varför är det så svårt att få produktägare att prioritera kvalitet istället för att trycka in massa funktioner hela tiden!” räcker upp en hand. — Ok ta ner.

Att hålla hög kvalitet är ingen trade-off eller kompromiss i systemutveckling. Det är inte ens förenat med en extra kostnad! Varför? Jo, för i systemutveckling går produktionstaken upp betydligt om systemet håller hög kvalitet. Har systemet dessutom ett test ramverk, som hjälper systemutvecklare att göra rätt, ökas möjligheten till kontinuerlig förbättring och därmed ytterligare kvalitet väsentligt. Att kvalitet skulle vara något man kan byta mot mer funktionalitet är därför helt felaktigt. Med högre kvalitet kommer istället mer funktionalitet. Det här skiljer sig ju från den gängse normen att “man får vad man betalar för”. Det fungerar inte riktigt på samma sätt i systemutveckling, här är det snarare: “Har man dålig kvalitet i sitt system får man betala mer!” och vilken produktägare skulle vilja betala mer för mindre om detta tydliggjordes?

Men hur får man då hög kvalitet i sitt system om man inte redan har det? Ja, det är här det börjar bli komplicerat. Hade detta varit enkelt hade det antagligen inte funnits system som håller dålig kvalitet, för alla vill ju göra ett bra jobb. Men det är en systemutvecklares skyldighet att införa förändringar som inte försämrar, utan snarare ständigt förbättrar kvalitet i de system de jobbar med. Advokater, Läkare och Mäklare är exempel på yrken som håller sig med förbund för att upprätthålla en god kvalitet i respektive bransch (den sistnämnda vet jag inte om de lyckas så bra). Här jobbar man ständigt med övergripande mål och förbättringar. Något motsvarande finns inte inom systemutveckling, men lika fullt är det viktigt att upprätthålla god kvalitet. Man kan dra det så långt som att det är viktigt för samhället och utvecklingen av landet att detta görs bra av oss som bor här – men det blir kanske lite högtravande.

Systemutveckling vs Lean manufacturing

På senare tid har det höjts fler och fler röster (jag är ingen journalist, så jag slänger mig med en sån, istället för fakta) som förespråkar att man använder principer från Lean manufacturing i sin systemutvecklingsprocess. Det är en mycket god idé, men långt ifrån en fullständig lösning. Jag ska försöka underbygga varför det inte fullständigt här.

Lean Manufactoring har en lång historia bakom sig. Toyota production system skapades under 35 år av Taiichi Ohno genom hans erfarenhet från tygfabriker innan han stod på golvet i Toyota. Det är därför ganska naivt att tro att vi på några år ska kunna hitta en perfekt process för systemutveckling. Idag sitter många organisationer fast i en vattenfallsinspirerad organisation med olika avdelningar för utveckling, test och drift. I en sådan organisation kan en bok om Lean software development kännas som vägen till himmelriket. Dock finns det anledning att vara försiktig.

Det är viktigt att komma ihåg vad målsättningen är med Lean Manufactoring. Att skapa ett system som reglerar produktionstaken efter efterfrågan och ger så stor effektivitet som möjligt i produktionen. Kan man relatera det mot syftet för att bedriva systemutveckling? Att införa förändringar i system för att skapa tjänster och funktioner som ger värde åt företag. Inte riktigt. Det finns en viktig komponent i systemutveckling som inte alls ingår i Lean manufacturing. Det är ganska uppenbart om man tittar på det sista ordet “manufacturing” – tillverkning kontra utveckling i det andra fallet. I tillverkning är det underförstått att själva utvecklingen redan är gjord. En bil är ju redan designad och konstruerad innan tillverkningen ens har påbörjats. Vad betyder då det? Jo dels att varje iteration behöver innehålla en god del modellering, forskning och innovation och dels att det behövs utrymme för betydligt mer kreativ frihet än i en fabrik som producerar bilar. Detta ingår inte alls som en komponent i Lean manufacturing, det till och med motarbetas. Vad blir konsekvensen av det? Ja alla som har suttit i utvecklingsprojekt med otydligt arkitektur och som saknar tid för kreativ innovation vet var som händer med systemet som produceras på lång sikt. Det bildas ett vårtsvin – ett system som är lappat och trixat med för att införa funktioner under tidspress.

Vad är då lösningen? Den som kommer på det vinner en resa till Bahamas, som han/hon får betala själv. Men en viktig sak är att titta på helheten och inte fokusera på att optimera saker långt ner i kedjan som med stor sannolikhet är en suboptimering. Datamodellering, processkartläggning för verksamheten kan enkelt reducera hela it system som kanske kostar en förmögenhet att underhålla. Refaktorering av ett system med för många datalager kan skära bort 80-90% av fullständigt irrelevant konverteringskod.

Taiichi Ohno säger att den värsta formen av “waste” är “waste som döljer waste”. Framtagningen av ett onödigt delsystem kan skapa massvis med waste som går att reducera enligt konstens alla regler, men om man tar bort hela delsystemet är alla förbättringar förgäves, Lean eller inte.

Summering: Principer från Lean är mycket bra! Men det är långt ifrån hela sanningen. Vi får inte glömma helheten i systemutveckling. Ordning på modeller, ordning på systemarkitektur och utrymme för kreativ innovation som kontinuerligt förbättrar systemen. Eric Evans sa detta på senaste JFokus: “Every release is a preparation for the next one”.