tag:blogger.com,1999:blog-326607822024-03-13T03:48:05.877+01:00Reader meet authorPeter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.comBlogger57125tag:blogger.com,1999:blog-32660782.post-70331850245739863822010-02-11T19:24:00.003+01:002010-02-11T20:13:37.723+01:00Hur jag använder DDD i mitt projekt<b>Jag har haft möjlighet att praktisera domändriven design i ett av mina uppdrag, och jag tänker försöka beskriva hur det är att jobba på det sättet, varför det kan leda till högre kvalitet och produktivitet samt dela med mig av några tips och erfarenheter.<br></b><br>Med "domän" i domändriven design menar vi verksamhetsområdet, det företaget ägnar sig åt. I ett av mina uppdrag har jag verkat inom domänen elektroniska passersystem där jag arbetat med en hyfsat stor produkt med några hundra tusen rader kod och några tiotal manår utveckling bakom sig, den interagerar med olika typer av användare och andra system med hjälp av en varierad flora av gränssnitt och är sedan länge i produktion med fler än tusen installationer runt om i världen. En liten del av den här modulariserade produkten handlar om att hantera bokning av saker som konferensrum, tvättmaskiner och tennisbanor. <br><br>Inom den här modulen kan vi snäva in domänbeskrivningen till bokning, rätt och slätt. Det råkar vara en utomordentligt bra domän för att introducera DDD i en organisation, och dessutom för att skriva en artikel om arbetet, eftersom det innehåller ett visst mått av komplexitet men samtidigt är någorlunda bekant för de flesta. Problemet vi var ute efter att lösa med vår modell är att hålla reda på vem som får boka vad och när, och dessutom att leverera information till andra delar av systemet som fysiskt styr passage genom dörrar, utifrån gjorda bokningar.<br><br>Den här modulen stod i tur att genomgå större förändringar av flera orsaker, men man kunde konstatera att den var väldigt svår att jobba med, utan något entydigt centrum för affärslogik som man kunde studera och testa. Det var utspritt över hela det vertikala ledet, från JSP-sidor till SQL-satser. Genomgående var att typningen var väldigt primitiv, så man kunde se metodsignaturer som innehöll både tre, fyra och fem <font face="courier new">long</font> som ibland var primärnycklar och ibland millisekunder. <br><br><font face="courier new">PeriodConfiguration getPeriodConfiguration(<b>long</b> personId, <br> <b>long</b> resourceGroupId, <br> <b>long</b> bookersAccessCategoryId, <br> <b>long</b> from, <br> <b>long</b> to)</font><br><br>De klasser som fanns hade sällan något beteende eller några som helst begränsningar i vad de kunde innehålla, utan var normalt tomma databehållare med ganska yxigt (som det ofta blir) översatta engelska namn. Kort sagt, det fanns helt enkelt ingen egentlig modell, och det är tyvärr ingen ovanlig situation. <br><br>Det här ville vi förändra. Vi ville bygga en modell av hur bokningssystemet skulle fungera, som var förändringstålig och robust, och gick att testa och studera helt utan infrastruktur. En modell som vi kunde visa upp och resonera kring tillsammans med en domänexpert, kanske i skissform men allra helst hela vägen ner i koden. <br><br><h4>Domänexperten och det gemensamma språket</h4>Tillgång till en domänexpert är en av den viktigaste förutsättningen för att kunna bedriva domändriven design. Man kan och bör tillgodogöra sig så mycket baskunskap i ämnet som möjligt på egen hand, eftersom domänexperten ofta är en upptagen person, men för att verkligen få maximal utväxling i koden behöver man bolla med någon som verkligen vet hur det fungerar. Vårt team hade tur, vi har nämligen relativt god tillgång till en domänexpert som samtidigt är vår produktägare. Bokningsdomänen är som sagt rätt snäll, men som i varje domän och varje produkt finns det ett antal egenheter. I vårt fall handlar det om att känna till hur äldre och konkurrerande system används och vad de klarar av, vad som är efterfrågat bland användarna och hur interaktionen med existerande och framtida hårdvara ska fungera, bland annat.<br><br>Vår domänexpert tycker att det är roligt och givande att delta i modelleringsdiskussioner, och jag tror att det inte är alltför sällsynt om man utnyttjar tiden effektivt. Man bör komma väl förberedd utan att ha surrat fast sig vid bestämd idé på förhand. Ställ konstruktiva frågor som kan blottlägga avgränsningar och viktiga regler. "Förekommer det nånsin att...", "Kan man betrakta X och Y som olika varianter av Z?", "Händer det att A och B samtidigt är tomma?" och så vidare. Ge domänexperten utrymme, och undvik att hemfalla åt datatekniska termer utan var uppmärksam på hur han eller hon uttrycker sig. <br><br><br>En annan viktig ingrediens i domändriven design är det <i>gemensamma språket. </i>En modell som är både är djupt förankrad i hur domänen fungerar och som uttrycks i ett språk som används både av domänexperten, av programmerarna sinsemellan och som dessutom återfinns <i>i koden</i> blir väldigt förändringstålig. Ofta ligger det gemensamma språket nära fackspråket, men det kan även innehålla nya ord som behövs för att utrycka den typ av struktur man behöver för att skriva mjukvara, eller stryka några ord som betyder ungefär samma sak för att fokusera på ett enda med kristallklar betydelse. Vi baserade vårt språk i stor utsträckning på vad saker och ting hette i de användargränssnitt som redan fanns, eftersom det med tiden hade satt sig hos alla som kommit i kontakt med systemet. Min erfarenhet är att man ska akta sig för att försöka "rätta till" ologiska termer som är väl etablerade, eftersom det blir mycket svårare att upprätthålla koplingen mellan hur man pratar och hur koden är namngiven.<br><br>Men att hitta ett gemensamt språk är inte en helt och hållet passiv övning. Ibland identifierar man ett begrepp som kanske inte finns explicit i fackspråket eller i något gränssnitt, men som är väldigt användbart är man bygger en strukturerad modell som ett datorprogram. Ett bra exempel från vårt projekt är <i>bokningsförfrågan</i>. Både när man vill veta vad som är möjligt att boka och när man faktiskt utför en bokning så inkluderar det vem som bokar, vad man vill boka och när man vill boka det. Det här flöt liksom omkring i våra diskussioner och lät ungefär "<i>Ok, vi säger att en lägenhet vill boka tvättstuga 3 den 22:a oktober mellan 14.00 och 18.00...</i>", så vi introducerade det som ett explicit begrepp vilket betyder två saker: det är någonting som både programmerare och domänexpert är överens om vad det betyder, och det finns en klass i domänmodellen med samma namn:<br><br><font size="2"><font face="courier new"><font color="#000000">public class Bokningsförfrågan implements ValueObject<Bokningsförfrågan> {</font></font><br style="color:#000000;font-family:Courier New"><br style="color:#000000;font-family:Courier New"><font face="courier new"><font color="#000000"> Bokningsobjekt bokningsobjekt;</font></font><br style="color:#000000;font-family:Courier New"><font face="courier new"><font color="#000000"> Bokare bokare;</font></font><br style="color:#000000;font-family:Courier New"><font face="courier new"><font color="#000000"> Pass pass;</font></font><br style="color:#000000;font-family:Courier New"> ...<br style="color:#000000;font-family:Courier New"><font face="courier new"><font color="#000000">}</font></font></font><br style="font-family:Courier New"><br>Vi karaktäriserade det som ett värdeobjekt, <i>value object, </i>och speglade den språkliga definitionen i koden genom att konstruktorer och jämförelseoperationer som <font face="courier new">equals()</font> och <font face="courier new">hashcode()</font> garanterar att man aldrig stöter på en bokningsförfrågan som saknar exempelvis bokare, och där två bokningsförfrågningar med från samma bokare som avser samma bokningsobjekt vid samma tidpunkt i alla avseenden kan betraktas som lika. På så vis har vi höjt abstraktionsnivån från strängar och heltal till en klass med tydlig karaktär och beteende, och som speglar någonting som vi kan prata med domänexperten om. När önskemål om förändringar kommer i framtiden kommer man att kunna svara på dem mycket snabbare, och vår kod blir mer robust. <br><br>Domänmodellen är som synes programmerad på svenska i så stor utsträckning som möjligt, ett rätt så kontroversiellt beslut i teamet och tror jag bland programmerare i allmänhet. Jag var själv motståndare till det för inte så länge sedan, men min erfarenhet är att det absolut är värt att göra det om alla inblandade talar svenska. Översättningar blir sällan perfekta, och framför allt förlorar man den intima kopplingen mellan det talade och skrivna ordet man har om koden är på svenska. En övning till läsaren: heter det <i>a booking</i> eller <i>a reservation</i> på engelska?<br><br><h4>En kraftfull modell</h4>Nu ska vi titta på ett litet men matnyttigt exempel på hur man kan gå från ett uttalande av domänexperten till konkret kod om man har en kraftfull objektmodell, lättarbetade stödbibliotek och ett gemensamt språk. Så här skulle det kunna låta:<br><br><br><div style="text-align:center"><i>Man ska kunna ställa in ett bokningsobjekt så att man högst får ha till exempel tre aktiva bokningar under samma månad.</i><br></div><i><br><br></i>Det här är en av många bokningsregler som styr huruvida en bokning kan ske eller inte. Var och en av dessa regler implementerar gränssnittet <i>Bokningsregel</i> som kan en enda sak: att svara ja eller nej på om den tillåter en bokningsförfrågan <font size="1">(den skarpsynte anar kanske att det handlar om <a href="http://www.citerus.se/kunskap/pnehm/pnehmartiklar/specificationpatternasarefactoringtool.5.7f01ab8711670db755e80001298.html" id="hp-i" title="Specification">Specification</a>-mönstret)</font>. Vi behöver alltså titta på det aktuella bokningsobjektets inställningar, och om det finns en gräns för hur många bokningar en bokare får ha per månad, kontrollera att bokaren har högst så många bokningar på det här bokningsobjektet under den månad som passet infaller. Låter rätt enkelt, och definitivt någonting som man kan resonera kring tillsammans med domänexperten. Så här tydlig kan koden bli som faktiskt utför den här kontrollen:<br> <br><br><font face="courier new">public class MånatligBegränsning implements Bokningsregel {</font><br style="font-family:Courier New"><br style="font-family:Courier New"><font face="courier new"> @Override</font><br style="font-family:Courier New"><font face="courier new"> public boolean tillåter(Bokningsförfrågan bokningsförfrågan) {<br> Bokningsobjekt bokningsobjekt = </font><font face="courier new">bokningsförfrågan.bokningsobjekt();</font><br style="font-family:Courier New"><font face="courier new"> Regelinställningar regelinställningar = bokningsobjekt.inställningar();</font><br style="font-family:Courier New"><font face="courier new"> Integer gräns = regelinställningar.perMånadPerBokare();</font><br style="font-family:Courier New"><br><font face="courier new"> if (ejSatt(gräns)) {</font><br style="font-family:Courier New"><font face="courier new"> return true;</font><br style="font-family:Courier New"><font face="courier new"> } else {</font><br style="font-family:Courier New"><font face="courier new"> TimeInterval passetsMånad = månadFörPasstart(bokningsförfrågan.pass());<br> Bokare bokare = </font><font face="courier new">bokningsförfrågan.bokare();</font><br> <font face="courier new">List<Bokning> bokningarUnderMånad = bokare.listaAktivaBokningar(</font><font face="courier new">bokningsobjekt, </font><font face="courier new">passetsMånad</font><font face="courier new">);</font><br style="font-family:Courier New"><br style="font-family:Courier New"><font face="courier new"> return </font><font face="courier new">bokningarUnderMånad</font><font face="courier new">.size() < gräns;</font><br style="font-family:Courier New"><font face="courier new"> }</font><br style="font-family:Courier New"><font face="courier new"> }<br> <br> private boolean ejSatt(Integer gräns) {<br> return gräns == null;<br> }</font><br><br style="font-family:Courier New"><font face="courier new"> private TimeInterval </font><font face="courier new">månadFörPasstart</font><font face="courier new">(Pass pass) {</font><br style="font-family:Courier New"><font face="courier new"> TimePoint passStart = pass.somIntervall().start();</font><br style="font-family:Courier New"><font face="courier new"> CalendarDate datumFörPasstart = passStart.calendarDate(TIDSZON);</font><br style="font-family:Courier New"><br style="font-family:Courier New"><font face="courier new"> return datumFörPasstart.month().asTimeInterval(TIDSZON);</font><br style="font-family:Courier New"><font face="courier new"> }</font><br style="font-family:Courier New"><br style="font-family:Courier New"><font face="courier new">}</font><br style="font-family:Courier New"><br>Huvuddelen av jobbet görs av <font face="courier new">Bokare</font>-klassen, i den metod som listar alla bokningar för ett visst bokningsobjekt under ett intervall, exempelvis en kalendermånad. Den här koden ligger så nära domänexpertens förståelse av hur systemet fungerar att det nästan går att parprogrammera tillsammans. Vi har gått igenom och exekverat scenariotester på sprintavslut, vilket fungerade riktigt bra och var en välkommen omväxlig. Kanske inte något man gör varje gång, men det kan definitivt föra delar av organisationer närmare varandra och öka utomståendes förståelse och intresse för mjukvaruutveckling.<br><br><h4>Fokusera </h4>En sak som man brukar betrakta som del av det man kallar <i>strategisk design </i>är att att identifiera vad som är kärnan i verksamheten, <i>core domain</i>, och fokusera på det. Det låter självklart, men det är tyvärr väldigt vanligt att man lägger ner massor av arbete på saker som tillför liten eller ingen affärsnytta till produkten, antingen omedvetet eller för att man tycker att man hittat ett intressant problem. <br><br>För oss är kärnverksamheten (under det här projektet och i den här delen av produkten) det vi formulerade tidigare: att hålla reda på vem som får boka vad och när, och dessutom att leverera information till andra delar av systemet som fysiskt styr passage genom dörrar, utifrån gjorda bokningar. Företaget säljer väl integrerade helhetslösningar med både mjuk- och hårdvara, och allt måste mynna ut i att dörren till tennishallen öppnas det klockslag som bokningen är gjord. <br><br>Fundamentet i vår affärslogik är förstås starkt knutet till tidshantering - datum, månader, tidpunkter, intervall och så vidare. Det är dock inte något som är unikt för vår produkt, eller ens för bokningsdomänen. Det hör till programmeringens allmängods och ett exempel på vad man på engelska kallar <i>generic subdomain</i>. Här vill vi lägga så lite energi som möjligt och istället använda färdiga och kraftfulla bibliotek. Att hacka på tid- och datumhantering i Java är lite av spel mot öppet mål, men jag påminner ändå om hur det ofta ser ut när man försöker bygga något med standardbiblioteket:<br><br> <br><font face="courier new"> Calendar cal = getCalendar(start.getTimeInMillis());</font><br style="font-family:Courier New"><font face="courier new"> cal.set(Calendar.HOUR_OF_DAY, 0);</font><br style="font-family:Courier New"><font face="courier new"> cal.set(Calendar.MINUTE, 0);</font><br style="font-family:Courier New"><font face="courier new"> cal.set(Calendar.SECOND, 0);</font><br style="font-family:Courier New"><font face="courier new"> cal.set(Calendar.MILLISECOND, 0);</font><br style="font-family:Courier New"><font face="courier new"> long startTimeMidnight = cal.getTimeInMillis();</font><br style="font-family:Courier New"><br style="font-family:Courier New"><font face="courier new"> ... </font><br style="font-family:Courier New"><br style="font-family:Courier New"><font face="courier new"> if (start.getTimeInMillis() == startTimeMidnight || bp.getFromTime() >= start.getTimeInMillis()) {</font><br style="font-family:Courier New"><font face="courier new"> periods.add(bp);</font><br style="font-family:Courier New"><font face="courier new"> }</font><br style="font-family:Courier New"><br style="font-family:Courier New"><font face="courier new"> ...</font><br style="font-family:Courier New"> <br style="font-family:Courier New"><font face="courier new"> /* next day */</font><br style="font-family:Courier New"><font face="courier new"> start.set(Calendar.DAY_OF_YEAR, start.get(Calendar.DAY_OF_YEAR) + 1);</font><br style="font-family:Courier New"><font face="courier new"> startingPoint = Long.valueOf(start.getTimeInMillis());</font><br style="font-family:Courier New"> <br><br>Vi valde att jobba det väldigt trevliga Time and Money, ett fritt bibliotek som huvudsakligen är skrivet av Eric Evans, i väntan på att ett nytt och bättre standardbibliotek ska dyka upp. <br><br><div>I den bokningsregel som begränsar antal bokningar per månad behöver vi veta vilken månad som passet infaller. Ett pass börjar i en tidpunkt (<font face="courier new">TimePoint</font>), som är någonting med minimal längd och maximal precision, exempelvis 2009-09-09 09:09:09.000 GMT. Den tidpunkten infaller på något datum i varje tidszon, som är ett väldigt annorlunda begrepp än tidpunkt och representeras av en explicit typ, <font face="courier new">CalendarDate</font>.</div><br><div>I vårt exempel och i tidszonen GMT+1 innebär det 2009-09-09. Slutligen infaller ett datum såklart under någon månad, här september 2009, en instans av klassen <font face="courier new">Month</font>: <br><br><br> <font face="courier new"> TimePoint passStart = pass.somIntervall().start();</font><br style="font-family:Courier New"><font face="courier new"> CalendarDate datumFörPassStart = passStart.calendarDate(TIDSZON);</font><br style="font-family:Courier New"><font face="courier new"> Month månad = datumFörPassStart.month();</font><br style="font-family:Courier New"><br> <br>Vår klass <font face="courier new">Bokare</font> kan lista bokningar inom vilket tidsintervall som helst - en dag, en månad eller från idag och två veckor framåt, och på tre tydliga rader kod kan vi klara av att formulera vår fråga till <font face="courier new">Bokare</font>-klassen. Genom att utnyttja Time and Money frigör vi massor av utvecklingstid som vi istället kan lägga på det som verkligen är unikt i vår produkt, och koden kan hållas snygg och prydlig.<br></div><br><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com2tag:blogger.com,1999:blog-32660782.post-49158253560952014892009-10-30T21:18:00.003+01:002009-10-30T21:21:17.445+01:00Med domänen i centrumJag såg ett <a href="http://svt.se/2.22620/1.1731235/astrid_lindgrens_barnsjukhus_ser_over_rutinerna?lid=senasteNytt_275224&lpos=rubrik_1731235">inslag om överdosering av läkemedel</a> på Astrid Lindgrens Barnsjukhus på Aktuellt härom dagen där en barnläkare berättade om ett datorprogram som användes på sjukhuset:<br /><br /><span id="split_19873378" style="font-style: italic;">- Väldigt ofta som doktor tänker jag hur många milligram som patienten ska ha. Men i journalsystemet måste jag ordinera i volym eller antal tabletter. Så jag måste först tänka vad patienten i slutändan ska ha, och sedan gå tillbaka och tänka ut hur patienten ska få det. En önskan hade varit att direkt kunna ange milligram per kilo, och att patientens vikt skulle finnas med i systemet.</span><span style="font-style: italic;"><br /><br /></span>Vid något tillfälle hade man tagt fel på milligram och milliliter när man doserat smärtstillande, vilket ledde till en tio gånger för hög dos. Det här kunde fått oerhört allvarliga konsekvenser.<br /><br />Jag har visserligen ingen insyn i detaljerna kring utvecklingen av det här journalsystemet, men det är väl ingen vågad gissning att man inte arbetat tillsammans med någon som är expert på hur läkemedel doseras när man byggt systemet. Det här är ett smärtsamt tydligt exempel på när idéerna och principerna från domändriven design är avgörande.<br /><br />DDD handlar i grund och botten om att gå tillbaka och fråga sig varför man över huvud taget bygger mjukvara. Vad ska den användas till, vilken process ska den underlätta? Vi tar vår utgångspunkt i domänen, verksamhetsområdet, och låter den genomsyra arbetet och produkten.<br /><br />Som programmerare är vi förhoppningsvis experter på att utveckla mjukvara. Vi kan allt om polymorfism, hashnycklar och skillnaden mellan<span style="font-family:georgia;"> inner och outer join.</span> Däremot är vi väldigt sällan experter på den verksamhet där vår mjukvara ska användas, som i fallet ovan kanske kan beskrivas som journalföring eller helt enkelt medicin.<br /><br />Det finns alltså ett kunskapsglapp som vi kan tjäna väldigt mycket på om vi kan överbrygga det på ett effektivt sätt. Resultatet, menar jag, blir mjukvara som fungerar bättre och är lättare att förändra över tiden eftersom den är utformad på ett sätt som intimt hänger ihop med hur domänen fungerar.<br /><br />Jag tänkte visa hur ett arbetssätt inspirerat av DDD hade kunnat undvika att hamna i den här situationen, med en fiktiv berättelse om hur utvecklingen hade gått till.<br /><br /><span style="font-size:85%;"><span style="font-style: italic;">Notera att jag hittat på detaljerna i dialogen nedan utifrån vad jag läst i nyhetsartiklar, det ska inte ses som medicinska råd eller så. </span></span><br /><br /><span style="font-weight: bold;font-size:130%;" ><br />I den bästa av världar</span><br /><br />Vid något tillfälle kan man anta att man kommer fram till en user story i backloggen som ser ut såhär:<br /><ul><li>Som läkare vill jag kunna ange mängden läkemedel i en dos i patientens journal.<br /></li></ul> Teamet jobbar redan med en rik domänmodell som fångar upp och organiserar den affärslogik och komplexitet som är relevant för den omfattning av journalystemet man byggt så här långt, i ett väl isolerat och testbart lager av applikationen. Dessutom har man etablerat ett regelbundet samarbete med Doktor Andersson, där man lär sig om hur läkare arbetar med patienters journaler och verifierar att vissa antaganden man gjort är korrekta.<br /><br />Nu berör man för första gången området dosering av läkemedel, och tar upp ämnet till diskussion med Doktor Andersson.<br /><br /><div style="padding: 30px;"><br /><span style="font-style: italic;">Engagerad Utvecklare</span>: - I den här sprinten ska vi bygga en del funktioner för dosering av läkemedel, så vi skulle behöva veta lite om hur det går till.<br /><br /><span style="font-style: italic;">Doktor Andersson</span>: - Ja, alltså man brukar besluta om en dos utifrån patientens tillstånd förstås, och den förs in i journalen. Själva medicineringen sköts av sjuksköterskorna på avdelningen.<br /><br /><span style="font-style: italic;">EU</span>: - Hur kan en sådan...notering se ut? Säger man notering?<br /><br /><span style="font-style: italic;">DA</span>: - En ordinering, som det heter, kan vara t ex "Zeffix, IV, 20 mg/4h". Det betyder alltså läkemedlet Zeffix, intravenöst, var fjärde timme.<br /><br /><span style="font-style: italic;">EU</span>: - Ok. Hur går själva beslutsprocessen till, alltså för en läkare, när man väl har ställt diagnos?<br /><br /><span style="font-style: italic;">DA</span>: - Vi har ett system som heter FASS, eller det är väl egentligen en katalogisering av i princip alla godkända läkemedel, där man kan söka och bläddra bland läkemedel utifrån vad man behöver behandla och så vidare. Det finns kategorier och underkategorier med ATC-koder. Sånt brukar ni systemvetare vara intresserade av.<br /><br /><span style="font-style: italic;">EU</span>: -Vi är inte systemv...hrm, ok. Har varje läkemedel en egen, unik ATC-kod?<br /><br /><span style="font-style: italic;">DA</span>: - Nej, det kan finnas några likartade alternativ för en viss ATC-kod. Ibland finns det bara ett.<br /><br /><span style="font-style: italic;">EU</span>: - Vad står i FASS om varje läkemedel som är relevant för ordinering då?<br /><br /><span style="font-style: italic;">DA</span>: - Väldigt mycket. Användningsområde, biverkningar, olika typer av riskfaktorer och rekommenderad dos.<br /><br /><span style="font-style: italic;">EU</span>: - Hmm...i det här exemplet med Zeffix står det 20 mg, men hur vet man att det ska vara just så mycket? FASS borde väl rimligtvis säga något om koncentrationen, eller?<br /><br /><span style="font-style: italic;">DA</span>: - Visst, så är det. Man ordinerar alltid verksam substans i milligram per kilo kroppsvikt, så den dosen är individuell och beror på patientens vikt. Dessutom varierar det mellan barn och vuxna, och ibland för gravida och liknande. En sak som skulle vara till otroligt stor hjälp är om man kunde ha någon sorts automatisk omvandling från milligram till det sätt som läkemedlets mängd anges, alltså i milliliter för flytande form, antal tabletter om det kommer i tablettform och så vidare.<br /><br /><span style="font-style: italic;">EU</span>: - Mm, jag förstår. Kan man säga något om maximal dos? Minimal? Kan man över huvud taget underdosera?<br /><br /><span style="font-style: italic;">DA</span>: - Jodå, man kan underdosera. Det för en rad problem med sig, som vi kanske inte behöver gå igenom i detalj, men det är i alla fall inte bra. FASS säger inget om maximal eller minimal dos, det är upp till läkaren att besluta.<br /><br /><span style="font-style: italic;">EU</span>: - Hur mycket brukar det variera? Hur ofta ger man mer än dubbla rekommenderade dosen till exempel? Vore det önskvärt om systemet kunde avgöra om dosen är osedvanligt hög eller låg, och varna eller blockera? Hur skulle man kunna avgöra det i så fall?<br /><br />DA: - Hmm...det är inte alls ovanligt att man doserar annorlunda än rekommendationen, i viss utsträckning, men det beror förstås på läkemedlet och en del annat. Det vore bra om man åtminstone kunde se hur dosen förhåller sig till rekommendationen, i procent eller liknande.<br /></div><br />Så här fortsätter samtalet. Under tiden har man skissat ihop det här på whiteboarden:<br /><br /><img src="http://vwe.petrix.se/%7Epeter/Scan.jpeg" /><br /><br />Efter mötet med domänexperten sätter sig två personer i teamet ner och parkodar fram ett utkast på hur ordinering av Zeffix till en patient kan se ut, med utgångspunkt i vad man nyligen har lärt sig och det språk som använts under samtalen.<br /><br /><pre class="brush: java"><br />@Test<br />public void ordinationEnligtRekommendation() {<br /> Läkemedel läkemedel = FASS.slåUpp("Zeffix");<br /><br /> Personnummer personnummer = new Personnummer(new LocalDate(1950, 1, 2), 1234);<br /> Amount<Mass> kroppsvikt = valueOf(75, KILOGRAM);<br /> Patient patient = new Patient(personnummer, kroppsvikt);<br /><br /> Koncentration koncentration = Koncentration.mgPerKgKroppsvikt(20);<br /> Frekvens frekvens = Frekvens.ggrPerDygn(4);<br /> Ordination ordination = new Ordination(läkemedel, koncentration, frekvens);<br /><br /> Dos dos = patient.dosVidOrdination(ordination);<br /><br /> assertThat(dos.läkemedel(), is(läkemedel));<br /><br /> Amount<Volume> förväntadMängd = valueOf(300, 0.001, MILLI(LITER));<br /> assertTrue(dos.mängdPerTillfälle().approximates(förväntadMängd));<br /><br /> assertThat(dos.relativtRekommendation(), is(1.0));<br /> assertFalse(dos.överdosering());<br /> assertFalse(dos.underdosering());<br />}<br /></pre><br /><br />(Den kompletta (körbara) koden <a href="http://github.com/peterbacklund/mdic">finns på Github</a> för den som är intresserad) <br /><br />Här kan man tydligt se hur språk, modell och kod hör ihop på ett intimt sätt, och man har fokuserat på att verkligen lösa ett problem i domänen på ett sätt som stämmer överens med domänexpertens uppfattning av hur det fungerar.<br /><br />Teamet kan nu ta med sig funderingar och frågor som dykt upp under programmeringsfasen in till nästa modelleringsmöte, som till exempel hur man hanterar läkemedel som kommer i tabellform, något som den första versionen av modellen inte klarar av.<br /><br /><br /><span style="font-weight: bold;font-size:130%;" >Vad av det här är DDD?</span><br /><br />Domändriven design är inget ramverk och ingen process, utan brukar lite luddigt beskrivas som ett förhållningssätt till att utveckla mjukvara, en uppsättning principer, en filosofi. Det är många små beståndsdelar i samverkan, och i exemplet ovan har jag försökt illustrera några av de viktigaste. Här sammanfattar jag dem i punktform:<br /><ol><li><span style="font-weight: bold;">Sätt domänen i centrum och prata med en domänexpert</span><br /><br />Leta reda på en person som har lång erfarenhet av domänen, som vet hur verksamheten fungerar och varför. Försöka utvinna så mycket kunskap som möjligt från den här personen för att kunna fatta bättre beslut vid designen av koden. <br /><br /></li><li><span style="font-weight: bold;">Utforma ett gemensamt språk</span><br /><br />Identifiera viktiga termer och begrepp i domänexpertens sätt att uttrycka sig. Säkerställ att ni är överens om betydelsen. Inför nya begrepp om det behövs.<br /><br /></li><li><span style="font-weight: bold;">Bygg en modell<br /></span><br />Ställ upp tänkbara scenarier. Peka på rutorna på whiteboarden och förklara högt hur de ingående delarna kan kombineras för att lösa problemet. Om nåt känns avigt, tänk om, förändra modellen. Experimentera.<br /><br /></li><li><span style="font-weight: bold;">Representera modellen i kod<br /></span><br />En modell som inte fungerar när man implementerar den i kod är i princip värdelös, så börja programmera så fort som möjligt. Jobba testdrivet genom att ställa upp scenarier med hjälp av testklasser. Använd det gemensamma språket för att döpa klasser, metoder och paket. Sträva efter att få koden att berätta vad den håller på med på ett sätt som en domänexpert kan förstå.<br /><br /></li><li><span style="font-weight: bold;">Använd byggstenarna</span><br /><br />DDD handlar till stor del om att utnyttja kraften i objektorientering, och lyfter fram ett antal designmönster som stöd för att organisera koden i modellen och hålla nere komplexiteten. Studera och använd byggstenar som entity, value object, repository med flera.<br /><br /></li><li> <span style="font-weight: bold;">Fokusera på det viktiga</span><br /><br />Fundera på vad som verkligen är viktigt och unikt för den här produkten, och fokusera på det. För alla områden som är relaterade till men inte unika för den här domänen, utnyttja någon annans arbete. Saker som tid- och datumhantering eller manipulation av enheter och stoheter, massa och volym har andra med stor sannolikhet redan stött på och byggt verktyg för.<br /><br /></li><li><span style="font-weight: bold;">Återkoppla erfarenheter från koden</span><br /><br />När man omsätter kunskap och modell i konkret kod dyker nya utmaningar och frågor upp. Använd dem för att förändra och förbättra modellen, och ta med dem till nästa möte med domänexperten för att få stöd att fatta bättre designbeslut.<br /></li></ol><br /><br />Reklam: Citerus gillar DDD och vi vill gärna dela med oss av vår kunskap. Kika in på <a href="http://www.citerus.se/ddd">citerus.se/ddd</a> för mer information.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0tag:blogger.com,1999:blog-32660782.post-10784642605648478172009-10-29T21:35:00.005+01:002009-10-29T23:40:08.227+01:00Geeky fact of the day, provenA few weeks back, my colleague <a href="http://weakreference.blogspot.com/2009/09/geeky-fact-of-day.html">Patrik Fredriksson</a> turned my attention to a "Geeky fact of the day" <a href="http://twitter.com/joshbloch/statuses/4080292481">tweet</a> by Josh Bloch by verifying Josh's claim for a small number of integers using Clojure. Having recently picked up a discrete mathematics book in an attempt to refresh my academic skills, I found an exercise that wanted you to prove exactly that, so I gave it a shot.<br /><br />Clojure is great, no doubt about that, but mathematics also has its strong points: it's very stable, tool support is great and it scales tremendously well :-)<br /><br />So, we want to prove that the sum of the n first cubes is equal to the sum of the first n positive integers squared:<br /><pre><br /><span style="color: rgb(0, 0, 0);">1</span><sup style="color: rgb(0, 0, 0);">3</sup><span style="color: rgb(0, 0, 0);"> + ... + n</span><sup style="color: rgb(0, 0, 0);">3</sup><span style="color: rgb(0, 0, 0);"> = (1 + ... + n)</span><sup style="color: rgb(0, 0, 0);">2</sup><br /></pre><br />I'm going to use the principle of induction, which means that you start out with a concrete, simple case and show that the theorem holds for that. Then you assume that it's true for some arbitrary value k and show that the theorem then holds for <span style="font-family: courier new;">k + 1</span>. By virtue of a domino effect from your base case, the theorem is proven for all natural numbers.<br /><br />Let's start with the induction basis:<br /><pre><br /><span style="color: rgb(0, 0, 0);">1</span><sup style="color: rgb(0, 0, 0);">3</sup><span style="color: rgb(0, 0, 0);"> = 1 = 1</span><sup style="color: rgb(0, 0, 0);">2</sup><br /></pre><br />So, it's obviously true for <span style="font-family: courier new;">n = 1</span>. Now for the induction hypothesis - assume that the theorem holds for <span style="font-family: courier new;">n = k</span>:<br /><pre><br /><span style="color: rgb(0, 0, 0);">1</span><sup style="color: rgb(0, 0, 0);">3</sup><span style="color: rgb(0, 0, 0);"> + ... + k</span><sup style="color: rgb(0, 0, 0);">3</sup><span style="color: rgb(0, 0, 0);"> = (1 + ... + k)</span><sup style="color: rgb(0, 0, 0);">2</sup><br /></pre><br />Let's take a look at the right hand side expression evaluated for <span style="font-family: courier new;">k + 1</span>:<br /><pre><br /><span style="color: rgb(0, 0, 0);"> ((1 + ... + k) + (k + 1))</span><sup style="color: rgb(0, 0, 0);">2</sup><span style="color: rgb(0, 0, 0);"> =</span><br /><span style="color: rgb(0, 0, 0);">= (1 + ... + k)</span><sup style="color: rgb(0, 0, 0);">2</sup><span style="color: rgb(0, 0, 0);"> + (k + 1)</span><sup style="color: rgb(0, 0, 0);">2</sup><span style="color: rgb(0, 0, 0);"> + 2(k + 1)(1 + ... + k) =</span><br /><span style="color: rgb(0, 0, 0);">= (1 + ... + k)</span><sup style="color: rgb(0, 0, 0);">2</sup><span style="color: rgb(0, 0, 0);"> + (k + 1)</span><span style="color: rgb(0, 0, 0); font-weight: bold;">((k + 1) + 2(1 + ... + k))</span><br /></pre><br />I'm using the familiar expansion of <span style="font-family: courier new;">(a + b)</span><sup style="font-family: courier new;">2</sup> and then factoring out <span style="font-family: courier new;">(k + 1)</span> from the last two terms. Focusing for a moment on the factor in bold:<br /><pre><br /><span style="color: rgb(0, 0, 0);"> ((k + 1) + 2(1 + ... + k)) =</span><br /><span style="color: rgb(0, 0, 0);">= ((k + 1) + 2(1 + ... + k - 1) + 2k) =</span><br /><span style="color: rgb(0, 0, 0);">= (k + </span><span style="color: rgb(0, 0, 0); font-weight: bold;">2(1 + ... + k - 1)</span><span style="color: rgb(0, 0, 0);"> + 1 + 2k)</span><br /></pre><br />Narrowing in on the second term in this expression, we can use a clever trick:<br /><pre><br /><span style="color: rgb(0, 0, 0);"> 2(1 + ... + k - 1) =</span><br /><br /><span style="color: rgb(0, 0, 0);">= 1 + 2 + ... + k - 1 +</span><br /><span style="color: rgb(0, 0, 0);">+ k - 1 + k - 2 + ... + 1 =</span><br /><br /><span style="color: rgb(0, 0, 0);">= (k - 1 + 1) + (k - 2 + 2) + ... + (1 + k - 1)</span><br /></pre><br />Using this symmetry we can deduct that<br /><pre><br /><span style="color: rgb(0, 0, 0);">2(1 + ... + k - 1) = k(k - 1)</span><br /></pre><br />since there are <span style="font-family: courier new;">k - 1</span> expressions each evaluating to <span style="font-family: courier new;">k</span> in the summation table above. Injecting this back we have<br /><pre><br /><span style="color: rgb(0, 0, 0);">k + </span><span style="color: rgb(0, 0, 0); font-weight: bold;">k(k - 1)</span><span style="font-weight: bold; color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">+ 1 + 2k = k</span><sup style="color: rgb(0, 0, 0);">2</sup><span style="color: rgb(0, 0, 0);"> + 1 + 2k</span><br /></pre><br />And this in turn back into the full right hand side expression:<br /><pre><br /><span style="color: rgb(0, 0, 0);"> (1 + ... + k)</span><sup style="color: rgb(0, 0, 0);">2</sup><span style="color: rgb(0, 0, 0);"> + (k + 1)</span><span style="color: rgb(0, 0, 0); font-weight: bold;">(k<sup>2</sup> + 1 + 2k)</span><span style="color: rgb(0, 0, 0);"> =</span><br /><span style="color: rgb(0, 0, 0);">= (1 + ... + k)</span><sup style="color: rgb(0, 0, 0);">2</sup><span style="color: rgb(0, 0, 0);"> + (k + 1)(k + 1)</span><sup style="color: rgb(0, 0, 0);">2</sup><span style="color: rgb(0, 0, 0);"> =</span><br /><span style="color: rgb(0, 0, 0);">= (1 + ... + k)</span><sup style="color: rgb(0, 0, 0);">2</sup><span style="color: rgb(0, 0, 0);"> + (k + 1)</span><sup style="color: rgb(0, 0, 0);">3</sup><br /></pre><br />But<br /><pre><br /><span style="color: rgb(0, 0, 0);">(1 + ... + k)</span><sup style="color: rgb(0, 0, 0);">2</sup><span style="color: rgb(0, 0, 0);"> = 1</span><sup style="color: rgb(0, 0, 0);">3</sup><span style="color: rgb(0, 0, 0);"> + ... + k</span><sup style="color: rgb(0, 0, 0);">3</sup><br /></pre><br />according to the hypothesis, so<br /><pre><br /><span style="color: rgb(0, 0, 0);">(1 + ... + k)</span><sup style="color: rgb(0, 0, 0);">2</sup><span style="color: rgb(0, 0, 0);"> + (k + 1)</span><sup style="color: rgb(0, 0, 0);">3</sup><span style="color: rgb(0, 0, 0);"> = 1</span><sup style="color: rgb(0, 0, 0);">3</sup><span style="color: rgb(0, 0, 0);"> + ... + k</span><sup style="color: rgb(0, 0, 0);">3</sup><span style="color: rgb(0, 0, 0);"> + (k + 1)</span><sup style="color: rgb(0, 0, 0);">3</sup><br /></pre><br />which is the left hand side of the original expression, for <span style="font-family: courier new;">k + 1</span>. This means that the induction hypothesis holds, and the theorem is proven.<br /><br />Q.E.D.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0tag:blogger.com,1999:blog-32660782.post-46424979462939997362009-04-17T20:59:00.008+02:002009-09-21T16:02:01.802+02:00Running Spring on Google App EngineIn case you've been living under a rock the last couple of weeks, Google recently announced the addition of <a href="http://code.google.com/appengine/docs/java/overview.html">Java support</a> to its App Engine. I have written a small sample application that leverages Spring and each of the Google <a href="http://code.google.com/appengine/docs/java/apis.html">infrastructure services</a> that are exposed either as proprietary APIs or serves as backend to standard APIs.<br /><br />The application itself, <a href="http://feelingluckypictures.appspot.com/">Feeling Lucky Pictures</a>, is very simple: you login using your Google account, and you import images from URLs into your personal gallery. You can also send an email with your pictures attatched.<br /><br />Integration with the Google infrastructure is as follows:<br /><ul><li>Authentication is of course done with the <a href="http://code.google.com/appengine/docs/java/users/">Google Accounts API</a>.<br /><br /></li><li>Image import uses a regular java.net.URL input stream, but on the GAE runtime that class is <a href="http://code.google.com/appengine/docs/java/urlfetch/usingjavanet.html">backed by</a> the URL Fetch API.<br /><br /></li><li>Storage is implemented using JDO, which is <a href="http://code.google.com/appengine/docs/java/datastore/usingjdo.html">backed by</a> a BigTable data store.<br /><br /></li><li>Reading objects from the data store uses the <a href="http://code.google.com/appengine/docs/java/memcache/usingjcache.html">JSR 107 javax.cache API</a>, which is backed by <a href="http://code.google.com/appengine/docs/java/memcache/">Memcache</a>.<br /><br /></li><li>Imported images are enhanced with the <span style="font-style: italic;">I'm feeling lucky</span> filter, part of the <a href="http://code.google.com/appengine/docs/java/images/overview.html">Images API</a>.<br /><br /></li><li>Email is sent with the standard <a href="http://code.google.com/appengine/docs/java/mail/usingjavamail.html">JavaMail API</a>, which also is backed by a custom mail service on GAE.</li></ul>As I said, the application is built on Spring as I wanted to see what problems, if any, I would run into if I were to run a regular Spring application on GAE. Most things worked out fine, but there were a handful of issues that I discovered and resolved, which is the real value of this application.<br /><ul><li>I decided to make as heavy use of the annotation configuration option as possible, including automatic classpath scanning. However, Spring will then attempt to scan for JPA annotations if certain conditions are met, which in turn will cause the javax.naming.NamingException class to load.<br /><br />This class is not on the GAE <a href="http://code.google.com/appengine/docs/java/jrewhitelist.html">class whitelist</a>, and working around the missing class by providing a JNDI API jar or a dummy class won't work either. My solution was to roll a JDO-only version of the spring-orm jar. This problem is also discussed in <a href="http://groups.google.com/group/google-appengine-java/browse_thread/thread/187d41712ec1d394">this thread</a>, with alternative solutions.<br /><br /></li><li>When configuring the <a href="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/orm/jdo/LocalPersistenceManagerFactoryBean.html">LocalPersistenceManagerFactoryBean</a>, don't specify a configuration file property (i.e. "classpath:META-INF/jdoconfig.xml"). Instead, set the persistenceManagerFactoryName property to "transactions-optional", which is the name used in the default configuration file provided by the Eclipse GAE plugin.<br /><br /></li><li>I wanted to avoid using the various Google API <a href="http://code.google.com/appengine/docs/java/images/overview.html">static</a> <a href="http://code.google.com/appengine/docs/java/users/overview.html">factories</a> programmatically in my MVC controllers, instead injecting service interfaces like ImageService to improve testability. This worked fine for the most part, simply using the factory-method attribute in the bean definition, with the exception of the <a href="http://code.google.com/appengine/docs/java/memcache/usingjcache.html">Cache interface</a>.<br /><br />Cache extends java.util.Map, and for some reason the <a href="http://static.springframework.org/spring/docs/2.5.x/reference/beans.html#beans-autowired-annotation">@Autowire mechanism</a> requires generic key and value types on constructor parameters of Map type. Unfortunately the Cache interface is not possible to parameterize, so my workaround was to embed it in a very simple <a href="http://code.google.com/p/feeling-lucky-pictures/source/browse/trunk/src/rma/flp/CacheHolder.java">CacheHolder</a> one-property class, which is produced by a <a href="http://code.google.com/p/feeling-lucky-pictures/source/browse/trunk/src/rma/flp/CacheHolderFactoryBean.java">factory bean</a> and injected.<br /><br /></li><li>I'm caching picture ids per email address, like this: "foo@bar.com":[1,5,7]. In a regular in-memory HashMap cache you can add elements to a collection map value, but on GAE you need to overwrite the old entry with a new collection that contains the old elements plus the new one. See <a href="http://code.google.com/p/feeling-lucky-pictures/source/browse/trunk/src/rma/flp/JdoPictureRepository.java#102">JdoRepository</a> for details.<br /><br /></li><li>Spring provides a set of abstractions on top of JavaMail, which I wanted to keep. In particular, the <a href="http://static.springframework.org/spring/docs/2.5.x/api/org/springframework/mail/javamail/JavaMailSender.html">JavaMailSender</a> interface and MimeMessageHelper class, for sending emails with attachments. This turned out to be a bit cumbersome, since the GAE mail backend is wired into the implementation of the JavaMail API in an unusual way. There's no SMTP transport, for example.<br /><br />What I had to do was to override Spring's implementation of JavaMailSender and align creation and usage of javax.mail.Session and javax.mail.Transport exactly with <a href="http://code.google.com/appengine/docs/java/mail/overview.html">the howto instructions</a> for the GAE mail service. Basically you can't let the Transport connect and then send the message on the established connection, you need to do it all in one pass using the static Transport.send() method.<br /><br />Oh, and don't leave the body of an email empty, or you'll get a <a href="http://code.google.com/p/feeling-lucky-pictures/source/browse/trunk/src/rma/flp/SendEmailController.java#56">really poor</a> error message.<br /><br /></li></ul>Other than that it's business as usual. The complete source code is <a href="http://code.google.com/p/feeling-lucky-pictures/source/checkout">available on Google Code</a>, and it's MIT licensed so you can do whatever you want with it. If it helped you out, or if you've found a better or simpler solution to any of the problems above, please drop a comment on this article.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com16tag:blogger.com,1999:blog-32660782.post-27527167968542813142009-03-27T20:21:00.004+01:002009-09-21T16:02:01.803+02:00DDDSample 1.1.0 releasedWhat the title says :-)<br /><br />It's been 6 months since 1.0, and <a href="http://dddsample.sourceforge.net/changelog.html">quite a lot has happened</a>. If you're interested in domain-driven design, take a look at it and let us know what you think, either on the international <a href="http://tech.groups.yahoo.com/group/domaindrivendesign/">Yahoo DDD group</a> or the <a href="http://groups.google.com/group/dddsverige?hl=sv">Swedish Google group</a>.<br /><br />If you're interested in learning more about DDD, Citerus has <a href="http://www.citerus.se/tjanster/ddd.4.30c78e2811e644991e780001489.html">a variety of offers</a> for you and your team.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0tag:blogger.com,1999:blog-32660782.post-44396011206406175872009-03-23T17:24:00.007+01:002009-09-21T16:02:01.803+02:00Five tips for successfully deploying MavenMaven is one of those things that people seem to <a href="http://www.google.com/search?q=i+hate+maven">hate</a> rather intensely, but nevertheless adoption is steadily rising in the Java community. I've worked with Maven almost daily since the 1.0 betas, and here are five things that I think could help your team working more efficiently with Maven.<br /><br /><ol><li><span style="font-weight: bold;">Use a repository manager</span><br /><br />A repository manager is basically an on-demand mirroring repository cache that you set up inside your IT infrastructure and use as primary repository for your builds. They basically work like this: if you build a project that depends on, for example, commons-lang-2.4.jar, the repository manager will download the artifact from the main Maven repository on the web, cache it locally and return it to the build client that asked for it. All subsequent builds that use the same managed repository will get the commons-lang jar delivered from the cache, not from the web.<br /><br />This has many advantages. First of all, it's fast. All project members, except the first one, will download any given dependency at LAN speed, which is especially nice when you're setting up a build environment from scratch (new project member, staging a clean build, etc). And of course it saves external bandwidth for other purposes and to lower costs.<br /><br />Second, it's safer. It allows you to run centralized and incremental backups on all external dependencies that you projects use, and you reduce your dependency on the availability of public repositories.<br /><br />Third, it's convenient. From time to time you will need a library that's not (yet) available in any public repository, so you have to publish it somewhere. A repository manager makes that really easy. And if you're sharing internal libraries or interfaces between projects, it's extremely handy to deploy to the managed repository. You can even set up your continuous integration build to automatically deploy snapshots.<br /><br />I've had a pleasant experience working with <a href="http://nexus.sonatype.org/">Nexus</a>, but there are others. A repository manager should be as natural a part of you infrastructure as SCM and CI if you're using Maven.<br /><br /></li><li> <span style="font-weight: bold;">Specify plugin versions</span><br /><br />By default, Maven will automatically download a new version of any given plugin whenever there is one available. Given that Maven is 99% made up of plugins (there's even a <a href="http://maven.apache.org/plugins/maven-plugin-plugin/">plugin plugin</a>!), this is a potential point of breakage over time and in my opinion a design mistake.<br /><br />As of version 2.0.9, the default behaviour <a href="http://maven.apache.org/release-notes-older.html">is improved</a> by locking down the versions of the core plugins (where "core" is defined by <a href="http://maven.apache.org/plugins/index.html">this list</a>). However, you still need to explicitly define versions for all non-core plugins, and that can be done at the top level pom.xml in a hierarchial project using the <a href="http://maven.apache.org/pom.html#Plugin_Management">pluginManagement section.</a><br /><pre><br /><pluginManagement><br /> <plugins><br /> <plugin><br /> <artifactid>maven-assembly-plugin</artifactid><br /> <version>2.2-beta-2</version><br /> </plugin><br /> <plugin><br /> <artifactid>maven-antrun-plugin</artifactid><br /> <version>1.2</version><br /> </plugin><br /> </plugins><br /></pluginManagement><br /></pre><br />Do this for the plugins that you actually use. Note that for plugins with group id org.apache.maven.plugin, you can omit the groupId element.<br /><br />This will make your builds more stable and eliminate a fairly rare but very annoying and confusing set of problems.<br /><br /></li><li><span style="font-weight: bold;">Learn how to use the dependency plugin</span><br /><br />Maven introduced the concept of transitive depedencies to the Java community, and has been a source of confusion ever since. The <a href="http://maven.apache.org/plugins/maven-dependency-plugin/">dependency plugin</a> is an invaluable tool for analyzing the results of the dependency algorithm, and to handle dependencies in various ways. Here are a couple of things you can do with it:<ul><br /><li><pre>dependency:tree</pre>shows (you guessed it) the dependency tree for the project, what dependencies are being pulled in and why. It's a nice overview and can help you tweak the dependency structure by excluding artifacts or override versions and so on. Example output:<br /><pre><br />[INFO] +- org.apache.activemq:activemq-core:jar:5.2.0:compile<br />[INFO] | +- org.apache.camel:camel-core:jar:1.5.0:compile<br />[INFO] | +- org.apache.geronimo.specs:geronimo-jms_1.1_spec:jar:1.1.1:compile<br />[INFO] | +- org.apache.activemq:activeio-core:jar:3.1.0:compile<br />[INFO] | | \- backport-util-concurrent:backport-util-concurrent:jar:2.1:compile<br />[INFO] | \- org.apache.geronimo.specs:geronimo-j2ee-management_1.0_spec:jar:1.0:compile<br /></pre></li><li><pre>dependency:go-offline</pre>Downloads all project dependencies and plugins, transitively. It's a good command to run both if you want to work offline for a while and if you want to get as many of the external dependencies in place in a single shot with no manual intervention while you go grab a cup of coffee and/or read another item in <a href="http://java.sun.com/docs/books/effective/">Effective Java</a> ;-)<br /><br /></li><li><pre>dependency:copy<br />dependency:copy-dependencies</pre>If you ever need to handle artifacts as files, copying all or some of them to a custom location for whatever reason, this is a good approach.<br /><br /></li></ul>There are many more things you can do with it, and mastering it will help you get on top of the transitive dependency situation.<br /><br /></li><li><span style="font-weight: bold;">Use the documentation<br /></span><br />Well, duh. But a weak point of Maven in the eyes of many people is the lack of documentation and the sometimes poorly organized information. There are a few good points of reference though, that you can spread around you team by setting up links on the Wiki for example:<br /><br /><ul><li><a href="http://www.sonatype.com/books/maven-book/reference/">The Definitive Guide to Maven</a>: a free book from Sonatype, available both as HTML and PDF. Good for the beginner, and sometimes as a reference. If you don't know where to start, start here.<br /><br /></li><li>The <a href="http://maven.apache.org/plugins/index.html">plugin list</a>: a comprehensive list to the official plugins, with links to each project page and JIRA subsection. Most of the core functionality is actually performed by one of these plugins, and you can learn a lot by studying things like the <a href="http://maven.apache.org/plugins/maven-resources-plugin/">resources plugin</a> documentation.<br /><br /></li><li>The <a href="http://maven.apache.org/pom.html">POM reference</a>: for the slightly more advanced user. Every element in the POM is explained. Don't forget to specify the XSD information in your POM file to get the most help from your XML editor. <br /></li></ul><br /></li><li><span style="font-weight: bold;">Understand the conventions</span><br /><br />Maven is a conventions-based tool, relieving you from scripting common task like compiling source code, running tests or packaging a web application into a war file. Learning the conventions - directory structure, build phases - and working along them will make your life easier a lot of the time.<br /><br />There are definitely situations even in moderately sized projects to customize the build however, and Maven can sometimes be quite cumbersome to work with when you need to break the conventions. But by understanding the conventions and having the mindset that there is a good chance what you're trying to do can be accomplished within the realms of the conventions, you might be able to find a different approach than you otherwise might have.<br /><br />Perhaps that ugly jar-splitting, file-copying, token-replacing <a href="http://maven.apache.org/plugins/maven-antrun-plugin/">antrun</a> hack that you spent an agonizing week writing could be replaced by extracting part of the project into a separate module and included as a dependency instead? It's a lot easier to swim downstream than upstream.<br /><br /></li></ol>Maven is not perfect by any means, but it has brought standardization and conventions to the world of Java development. Project structure, directory structure, public metadata and artifact repository publishing to name a few. There are lots of plugins available, both central and third-party ones, and most IDEs and continuous integration servers support Maven very well.<br /><br />A lot of the standardization may even outlive the Maven tool itself, as demonstrated by two newer build system for Java: <a href="http://buildr.apache.org/">Buildr</a> and <a href="http://www.gradle.org/">Gradle</a>. They both use many of the same conventions, and could challenge Maven by perhaps being able to scale down in complexity more easily and have a lower threshold for newcomers. Progress on Maven slowed down a bit after 2.0, but recently the<a href="http://maven.apache.org/release-notes.html"> 2.1.0 version was released</a> with a number of important improvments, for example parallel resolution of depenencies.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com6tag:blogger.com,1999:blog-32660782.post-22454384511075755392009-01-16T16:46:00.003+01:002009-01-21T19:29:38.288+01:00New PNEHM articleI'm about to publish my next <a href="http://www.citerus.se/kunskap/pnehm.4.a939951052f7784358000755.html">PNEHM</a> article, a step-by-step port of a short algorithm written in Java to Groovy. For my Swedish readers, <a href="http://docs.google.com/Doc?id=df9mvfvz_42ccjpgvfc">here's a sneak peak</a> (the article is in Swedish).<br /><br /><span style="font-weight: bold;">UPDATE</span>: the article is <a href="http://www.citerus.se/kunskap/pnehm/pnehmartiklar/franjavatillgroovy.5.30c78e2811e644991e780001338.html">now published</a>. Enjoy!<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0tag:blogger.com,1999:blog-32660782.post-63009410965266941732008-12-18T15:13:00.002+01:002009-09-21T16:02:01.803+02:00DDDSample tutorial at QCon LondonI'm very proud to announce that I will be <a href="http://qconlondon.com/london-2009/presentation/Domain-Driven+Design+-+a+complete+example+in+a+current+technology+stack">giving a tutorial</a> on the <a href="http://qconlondon.com/london-2009/presentation/Domain-Driven+Design+-+a+complete+example+in+a+current+technology+stack">DDD sample application</a> at QCon London in March next year, together with my colleague <a href="http://weakreference.blogspot.com/">Patrik Fredriksson</a>. The presentation will be basically the same as <a href="http://www.jfokus.se/jfokus/tracks.jsp">the one we're giving</a> at the Swedish conference JFokus in January, but in English (obviously :-)).<br /><br />Hope to see you there! The tutorial will cover the upcoming second release of the application.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com1tag:blogger.com,1999:blog-32660782.post-28516374928584914722008-11-23T20:35:00.007+01:002009-09-21T16:02:01.803+02:00On layering in DDDSampleThere has been considerable interest in the <a href="http://dddsample.sf.net/">DDDSample</a> application on the <a href="http://groups.google.com/group/dddsverige?hl=sv">Swedish DDD user group mailing list</a> - people are scrutinizing the code, asking questions and raising concerns. This last week has been mostly about layers and packages, and I think this blog is a good forum to provide a little background and explain the rationale behind how the sample application is structured.<br /><br />When we first started working on the application, we used a fairly standard layering with a web user interface layer, a service layer with interfaces, implementations, transaction demarcation and so on, and a repository layer for persistence, implemented in Hibernate (roughly matching the DAO layer found in most applications). These layers all resided in their own package hirearchy. In addition to that, the domain model had its own package, although it wasn't a layer in the usual sense - it was used by several other layers, but calls did not pass through the domain model on to some other layer. This suited us well for the time being, since there were many other aspects of the application that required our immediate attention.<br /><br />As the application grew more mature, we began looking at how the structure of the application could illustrate important DDD concepts such as aggregates and isolating the domain. In his book, Eric Evans uses this diagram when talking about layered archicture:<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjUdyeZwzSRGTVx0JdHOeA4LlY9ulcc0ZpHlk3tJRfr4WQFf-_ODYlhdRmfjeT5tCvkiLZkx_hRYPFUpsW4n90HB8jfoaqTlkuzpiOyePQBgCsE4LCAIaIGzfT4Y-PWvFdAUrL3w/s1600-h/dddlayers.png"><img style="cursor: pointer; width: 400px; height: 228px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjUdyeZwzSRGTVx0JdHOeA4LlY9ulcc0ZpHlk3tJRfr4WQFf-_ODYlhdRmfjeT5tCvkiLZkx_hRYPFUpsW4n90HB8jfoaqTlkuzpiOyePQBgCsE4LCAIaIGzfT4Y-PWvFdAUrL3w/s400/dddlayers.png" alt="" id="BLOGGER_PHOTO_ID_5271946357238271474" border="0" /></a><br /><br />By far the most important layer is the domain layer, so we decided to take a closer look at the contents of our domain layer and the <span style="font-weight: bold;">domain</span> package. A pretty obvious decision was to place each aggregate in its own subpackage below <b>domain</b>, so we had <b>domain.cargo</b>, <b>domain.handling</b> and so on. Deciding which services were domain services and which were application services was harder, but we settled for a separation where domain services performed tasks that you could talk about with a domain expert, using the ubiquitous language. Signatures consisted completely of domain model types. In some cases, it was natural to place the interface of a domain service in the domain layer, but the implementation elsewhere.<br /><br />But the decision that would turn out to be the most controversial was placing the repository definitions (i.e. the interfaces) in the domain layer, alongside the aggregate root for which it was used to retrieve, store and search. The concept of an aggregate root is closely linked to that of a repository: all access to an aggregate is through the root, so consequently the repository works with aggregate roots, and there is one repository per aggregate root (and thus per aggregate). Also, repositories are expressed in the ubiquitous language.<br /><br />At this point our domain layer consisted of the domain model, separated into aggregates, domain services and one repository per aggregate, which we felt pretty good about (and still do). It expressed many important DDD concepts in a clear way, and it was slightly unorthodox compared to many mainstream designs. All this was located under the domain package.<br /><br />The question of what to do with the rest of the application remained. The other three layers are not as interesting from a DDD perspective, so we decided not to pursue the effort of organizing the rest of the code into per-layer-package hierarchies, but instead separate it from the domain package and organize it by a combination of technology and use cases.<br /><br />We did however consciously include two very different approaches to user interface exposure. The tracking web interface, which runs in the same JVM as the main application, is the low-overhead-thight-coupling way of doing it (in terms of layers and lines of code), where the MVC controller acts as application layer and calls the domain layer repository directly. The tracked cargo is thinly wrapped in the view rendering phase to make it easier to work with in a JSP EL environment.<br /><br />The booking web interface on the other hand can run in a different JVM and works against an RMI facade on top of the domain layer, passing custom DTOs back and forth. The point is that we don't generally recommend a mandatory, slavishly delegating application layer between the presentation and the domain layers. Sometimes the controller in the MVC layer can play the part of application just as well. Another important point here is that you should always shield the domain model objects from presentation requirements, and DTOs or a thin presentation wrapper and so on are good options for doing that.<br /><br />The top level package for all non-domain-layer code was named <b>application</b>, which turned out to be a bad idea (mine) since the name coincided with one the the layers in the picture above. For the record, the rationale behind it was "application" as in "computer program", i.e. everything about the code that wasn't part of the domain. A better name would have been something like <b>nondomain</b> or even <b>other</b>.<br /><br />This problem immediately became apparent when I presented the application to the New York DDD User Group, so a separate <b>ui</b> package was extracted for the web MVC controllers and supporting code. This actually turned out to make matters slightly worse, since we now had top-level packages with names matching three out of the four layers in the DDD layer model, immediatly leading people to ask for the missing <span style="font-weight: bold;">infrastructure</span> package. The discussions on the mailing list and other forums helped us realize that there is a need for an explicit infrastructure package, so the next version of DDDSample will include such a package containing the parts that we consider to be part of the infrastructure layer.<br /><br />It appears from the discussions on the Swedish user group mailing list that many people think of the infrastructure as being identical to the persistence aspect (the database and the O/R mapper), but we have a wider definition of infrastructure which also includes messaging, scheduling, thread pools, the Spring container, the servlet container and external services such as mail senders and in our case the routing team's graph path finding service.<br /><br />Looking at the picture above, the arrows between the layers actually illustrate the point quite well: the presentation, service and domain layers all work with the infrastructure layer, but it's important to realize that it doesn't mean that you should execute SQL statements in your JSP pages, but rather that each layer interacts with some part of the infrastructure. Also, the infrastructure layer can be used for passing asynchronous messages between layers.<br /><br />In general, we consider code and configuration files that we write in order to hook into the infrastructure to be part of the infrastructure layer - Hibernate repository implementations, HBM mapping files, Spring context definition files, the RoutingService implementation and so on. Sometimes the distinction is harder to make, such as having an application service implement the JMS MessageListener interface to act message-driven. <br /><br />As a rule of thumb, never state rules of thumb when it comes to software development. But if I were to do that anyway, I'd say that the infrastructure layer should be completely separable from the rest of the application by stubbing out external services, implementing persistence in-memory, and using synchronous calls or simple threads for messaging.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com1tag:blogger.com,1999:blog-32660782.post-87249239026407563812008-11-02T12:01:00.003+01:002009-09-21T16:02:01.803+02:00DDD updatesFor about a year now, I've been part of a team at <a href="http://www.citerus.se">Citerus</a> that has worked with <a href="http://www.domainlanguage.com/about/ericevans.html">Eric Evans</a> to create a <a href="http://dddsample.sourceforge.net/">sample application</a> showcasing domain-driven design, and we have recently released the first version to the public.<br /><br />Al least two porting efforts have already been initiated: <a href="http://www.nabble.com/dddsample-from-sf.net-td20083616.html">Qi4J</a> and <a href="http://fornax-platform.org/cp/x/RQk">Sculptor</a>. Some people are <a href="http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/10/22/where-are-the-ddd-sample-applications.aspx">blogging</a> and <a href="http://search.twitter.com/search?q=dddsample">twittering</a> about it, too :-)<br /><br />The <a href="http://groups.google.com/group/dddsverige?hl=sv">Swedish DDD user group</a> held its first meeting at the <a href="http://www.omegapoint.se/">Omegapoint</a> office in Stockholm, where <a href="http://weakreference.blogspot.com/">Patrik Fredriksson</a> and I presented the application. In August I <a href="http://www.meetup.com/dddnyc/boards/thread/5472759">presented</a> the application to the New York DDD user group, and in January 2009 we will give a <a href="http://www.jfokus.se/jfokus/speakers.jsp#Patrik%20Fredriksson%20och%20Peter%20Backlund">three-hour tutorial</a> on basic DDD and the sample application during the <a href="http://www.jfokus.se/jfokus/">JFokus</a> conference.<br /> <br />On tuesday, November 4, Eric Evans and Patrik Fredriksson will host a <a href="http://www.citerus.se/dddnov">one-day seminar</a> on DDD in Stockholm.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0tag:blogger.com,1999:blog-32660782.post-55923398670256572842008-04-11T12:50:00.002+02:002009-09-21T16:02:01.808+02:00DSL for time and money(Well, for time anyway)<br /><br />A couple of weeks ago, I did some work together with <a href="http://www.domainlanguage.com/about/ericevans.html">Eric Evans</a> when he came to Uppsala to give his excellent course in <a href="http://domaindrivendesign.org/">domain driven design</a>, which was co-hosted by <a href="http://www.citerus.se/tjanster/utbildningarochseminarier/seminarier/domanmodelleniarbetefyradagarsdjupgaendekursidomaindrivendesignnupasvenska.5.61632b5e117dec92f47800068887.html">Citerus and Patrik Fredriksson.</a><br /><br />Eric is the project leader of the <a href="http://timeandmoney.sourceforge.net/">Time and Money</a> Java library, which makes working with dates, time intervals, currencies and so on a breeze. However, inspired by <a href="http://groovy.dzone.com/news/domain-specific-language-unit-">this article</a> by Guillame Laforge, I wanted to see if I could create something similar by leveraging Groovy and the Time and Money library. These are a few simple examples that I came up with in an hour:<br /><br /><pre><br />println 1.minute<br /><br />=> '1 minute'<br /><br /><br />println 5.minutes + 1.minutes<br /><br />=> '6 minutes'<br /><br /><br />println "2003-05-16" + 3.weeks - 50.years<br /><br />=> 'Sat Jun 06 01:00:00 CET 1953'<br /></pre><br />Looking at these statements from top to bottom, we first have<br /><pre><br />1.minute<br /></pre><br />The number 1 is of course an instance of java.lang.Integer, a full-blown object. On that instance, we access something called minute, which kind of looks like a field, but is actually a JavaBean property thanks to Groovy's <a href="http://groovy.codehaus.org/Groovy+Beans">built-in support</a> for those. So what we really have is an invocation of Integer.getMinute(), a method that doesn't exist. But don't worry - here's how we can use metaprogramming to add that method to the Integer class:<br /><pre><br />Integer.metaClass.getProperty = {symbol -><br />switch (symbol) {<br /> case ["minute"]:<br /> return Duration.minutes(delegate)<br /> default:<br /> return null<br />}<br />}<br /></pre><br />In Groovy, every class has a corresponding open metaclass, the <a href="http://groovy.codehaus.org/ExpandoMetaClass">ExpandoMetaClass</a>, that may be used to dynamically add methods on classes. The method getProperty is invoked when a JavaBean property is accessed, and here we assign a closure to be evaluated on invocation. The closure recieves one argument, symbol, which is a String containing the name of the property accessed, in this case "minute". This particular case is chosen to be converted to a Time and Money datatype, Duration, by passing the delegate (that's the instance we're invoking the getter on, i.e. 1) to the appropriate factory method. The result is that a Duration instance is returned, representing one minute.<br /><br />Moving on to the next one, we have<br /><pre><br />5.minutes + 1.minute<br /></pre><br />The terms being added are familiar by now, although we need to expand the previous closure to this:<br /><pre><br />Integer.metaClass.getProperty = {symbol -><br />switch (symbol) {<br /> case ["minute"]:<br /> return Duration.minutes(delegate)<br /> case ["years", "quarters", "months", "weeks", "days", "hours", "minutes", "seconds"]:<br /> return Duration.getMethod(symbol, int).invoke(null, delegate)<br /> default:<br /> return null<br />}<br />}<br /></pre><br />Fortunately, the Time and Money library has nicely named methods that correspond exactly to how we want to express durations in this DSL, so we can be very efficient and use reflection invocation of factory methods. Oh, and it's very nice to be able to switch on lists, isn't it? :-)<br /><br />But there's one more thing to it: the overloading of the + operator. We've already established that both 1.minute and 5.minutes are instances of Duration, and luckily the Duration class already has a plus(Duration) method on it, that Groovy will <a href="http://groovy.codehaus.org/Operator+Overloading">automatically evalute</a>. It's not always the case that there is such a method available though, as we find out when we move on to the third case:<br /><br /><pre><br />"2003-05-16" + 3.weeks - 50.years<br /></pre><br />Evaluating from left to right, we're initially adding a String and a Duration, but String does not have any plus(Duration) method, so we're going to have to add that:<br /><br /><pre><br />String.metaClass.plus = {Duration duration -><br />return duration.addedTo(TimePoint.parseGMTFrom(delegate, "yyyy-MM-dd"))<br />}<br /></pre><br />Here, we're using the Time and Money API to represent the String as a TimePoint, to which a Duration may be added, producing another TimePoint. Continuing our evaluation, we now have to subtract a Duration from a TimePoint, which should be quite familiar by now:<br /><pre><br />TimePoint.metaClass.minus = {Duration duration -><br />return duration.subtractedFrom(delegate)<br />}<br /></pre><br />These are just a few examples, and the possibilities are vast.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com2tag:blogger.com,1999:blog-32660782.post-78854825697771232422008-04-10T20:33:00.003+02:002009-09-21T16:02:01.808+02:00Grails Pet Store 0.2 releasedI finally managed to wrap up a semi-stable milestone of <a href="http://code.google.com/p/grails-petstore/">Grails Pet Store</a>, and the roadmap is now available in the form of <a href="http://code.google.com/p/grails-petstore/issues/list?sort=milestone">tagged issues</a>. Hopefully there will be a live instance available Real Soon - watch this spot for updates.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0tag:blogger.com,1999:blog-32660782.post-69916102399258327742008-03-02T12:03:00.003+01:002009-09-21T16:02:01.808+02:00Podcast from JFokus availableThe JFokus presentations are finally available online. Both my presentation on Grails and my collegue Patrik Fredriksson's presentation on the specification pattern are <a href="http://www.abiliteam.com/ability/show/phcicuh/javaforum_c/speed.asp">available here</a>. Type your name, press login, then press Play on the next page. Don't ignore the yellow information box if you're running Mac :-)<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0tag:blogger.com,1999:blog-32660782.post-91786369073176244382008-02-26T19:54:00.003+01:002009-09-21T16:02:01.808+02:00Damn you DBUnit!I simply can't get over how powerful the Groovy XML and SQL support is, especially when you combine the two. Did you ever find yourself in the position where you wanted to convert a Hypersonic database to a <a href="http://www.dbunit.org/components.html">DBUnit dataset</a>? I did, and I told my co-worker, somewhat disgruntled, that "I bet this could be done with 30 lines of Groovy". Well, it could:<br /><br /><pre><br />import groovy.sql.Sql<br />import groovy.xml.MarkupBuilder<br /><br />def sql = Sql.newInstance("jdbc:hsqldb:my_db", "sa", "", "org.hsqldb.jdbcDriver")<br /><br />def sw = new StringWriter()<br />def xml = new MarkupBuilder(sw)<br /><br />xml.dataset {<br /> sql.eachRow "select * from system_tables where table_type != 'SYSTEM TABLE'", {<br /> table(name:it.TABLE_NAME.toLowerCase()) {<br /> sql.rows("select * from ${t}", { md -><br /> md.columnCount.times {<br /> column md.getColumnName(it + 1).toLowerCase() ?: ""<br /> }<br /> }).each { r -><br /> row {<br /> r.size().times {<br /> value r[it]<br /> }<br /> }<br /> }<br /> }<br /> }<br />}<br /><br />println sw<br /></pre><br />This is why I like Groovy - it's powerful, yet elegant.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0tag:blogger.com,1999:blog-32660782.post-32821554064953190462008-02-24T21:43:00.002+01:002009-09-21T16:02:01.809+02:00Groovy powerThe <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/package-summary.html" target="_blank">concurrent API</a> that was added to Java 5 is very powerful for sumbitting tasks to a worker thread pool, but when you combine it with the Groovy ability to implement single-method interfaces <a href="http://groovy.codehaus.org/Groovy+way+to+implement+interfaces" target="_blank">with closures</a> you have a real winner.<br /><br /><pre><br />import java.util.concurrent.Callable<br />import java.util.concurrent.Executors<br /><br />def executorService = Executors.newFixedThreadPool(4)<br /><br />def x = {<br /> 20.times {<br /> println "X"<br /> }<br />} as Callable<br /><br />def y = {<br /> 20.times {<br /> println " Y"<br /> }<br />} as Callable<br /><br />executorService.invokeAll([x, y])<br /><br />executorService.shutdown()<br /></pre><br />which of course has an output similar to this:<br /><pre><br />X<br /> Y<br /> Y<br /> Y<br />X<br /> Y<br />X<br /> Y<br />X<br /> Y<br /> Y<br />X<br />X<br /> Y<br />X<br /> Y<br /> Y<br />X<br />X<br />X<br /></pre><br />Pretty neat, huh?<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0tag:blogger.com,1999:blog-32660782.post-91653574549512441332007-11-30T20:30:00.000+01:002007-11-30T20:37:14.953+01:00Shameless plugFor all my swedish readers who aren't either working for <a href="http://www.citerus.se">Citerus</a> or are regular guests in the same IRC channel as I am (should amount to about zero people, I'm afraid) - <a href="http://www.citerus.se/kunskap/pnehm/pnehmartiklar/vivalaevolucion.5.7f01ab8711670db755e80002738.html">here's an introductory article</a> on the excellent <a href="http://www.grails.org">Grails</a> framework that I've written for <a href="http://pnehm.citerus.se/">PNEHM</a>, Citerus' newsletter on agile development. <br /><br />I will also host a short (20 minutes) oral presentation on the same subject at the upcoming <a href="http://www.jfokus.se/jfokus/">JFokus</a> conference, in January.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0tag:blogger.com,1999:blog-32660782.post-37118991641155818502007-09-18T08:37:00.000+02:002007-09-18T09:29:51.931+02:00Advisor summaryIt's been a while since the last post, I've been busy with my new assignment that begun right after my vacation. Anyway, here a nifty little routine to quickly get an overview of which beans are woven by what advice, and also what advice weaves which beans in a Spring context: <br /><br /><pre><br />ApplicationContext context = ... ; // Create your context <br /><br />String perBeanSummary = "--- Advisors per bean ---\n";<br />Map<String,Advised> beanMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, Advised.class);<br />Map<Advisor,Set<String>> advisorMap = new HashMap<Advisor, Set<String>>();<br /><br />// This gathers advisors per bean and beans per advisors,<br />// and builds the presentation of advisors per bean <br />for (String beanName : beanMap.keySet()) {<br /> Advised advised = beanMap.get(beanName);<br /> perBeanSummary += beanName + ":\n\t";<br /> for (Advisor advisor : advised.getAdvisors()) {<br /> perBeanSummary += advisor.getAdvice().getClass().getName() + "\n";<br /> Set<String> beans = advisorMap.get(advisor);<br /> if (beans == null) {<br /> beans = new HashSet<String>();<br /> advisorMap.put(advisor, beans);<br /> }<br /> beans.add(beanName);<br /> }<br /> perBeanSummary += "\n";<br />}<br /><br />// Builds the presentation of beans per advisor<br />String perAdvisorSummary = "+++ Beans per advisor +++\n";<br />for (Advisor advisor : advisorMap.keySet()) {<br /> perAdvisorSummary += advisor.getAdvice().getClass().getName() + "\n";<br /> for (String beanName : advisorMap.get(advisor)) {<br /> perAdvisorSummary += "\t" + beanName + "\n";<br /> }<br /> perAdvisorSummary += "\n";<br />}<br /><br />System.out.println(perBeanSummary);<br />System.out.println(perAdvisorSummary);<br /><br /></pre><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0tag:blogger.com,1999:blog-32660782.post-84469471970483723812007-08-13T16:29:00.001+02:002007-08-13T16:43:00.256+02:00Grooeat work, JetBrains!The first day after the vacation was fairly productive, after all. In preparation for an upcoming <a href="http://pnehm.citerus.se/">PNEHM</a> article, I've successfully installed a snapshot of IDEA 7.0 and built a Subversion snapshot of the <a href="http://www.jetbrains.net/confluence/display/GRVY/Groovy+Home">Groovy/Grails plugin</a>. This is the first thing I tried out - it looks very promising:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizEtnkJsrVf9NNN3GzPNbK271iu_GvrslrHaTUBqaRxhVodHDs5smeHvbtQJyBi0yw4mNnLnpCzfQig5VZDeTkNqe5v16tRKkNyDt_6y8miCY_oEPhjB81KzClh-hYNh-3BJMI7Q/s1600-h/grails.jpeg"><img style="cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizEtnkJsrVf9NNN3GzPNbK271iu_GvrslrHaTUBqaRxhVodHDs5smeHvbtQJyBi0yw4mNnLnpCzfQig5VZDeTkNqe5v16tRKkNyDt_6y8miCY_oEPhjB81KzClh-hYNh-3BJMI7Q/s400/grails.jpeg" alt="" id="BLOGGER_PHOTO_ID_5098192882302579826" border="0" /></a><br />Dynamic typing <span style="font-style: italic;">and</span> completion!<br /><br /><span style="font-style: italic;">Update</span>: it turns out IDEA also completes the <a href="http://groovy.codehaus.org/groovy-jdk.html">Groovy additions to the JDK</a>. Not completely unexpected when you've seen the screenshot above, but nevertheless very nice!<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0tag:blogger.com,1999:blog-32660782.post-73391975462336808312007-07-12T15:38:00.000+02:002007-07-18T10:58:57.662+02:00Implementing ActiveRecord in JavaThe ActiveRecord (AR) design pattern is very popular right now, forming the base of web application frameworks such as Ruby on Rails and the Groovy-based <a href="http://grails.org/GORM+-+CRUD">Grails</a><a href="http://grails.org/GORM+-+CRUD">.</a> Martin Fowler <a href="http://www.martinfowler.com/eaaCatalog/activeRecord.html">defines</a> the pattern as follows:<br /><em><br />An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.<br /></em><br />AR is very closely related to the concept of an Object-Relational Mapper (ORM), and is an alternative to the Data Access Object (DAO) and Repository patterns. In the latter two patterns, persistent data operations are separated from the domain object into one or more dedicated interfaces.<br /><br />AR is very attractive if you want to build a more powerful domain model, as opposed to using it as a simple data container. It ties the most obvious domain logic into the domain object (namely CRUD), and opens up possibilities to write higher-level properties and operations on your domain objects.<br /><br />It can also aid in <a href="http://en.wikipedia.org/wiki/Database_normalization">normalizing </a>the three-tier architecture, by which I mean avoiding the all too common situation where you have a request mapped to an object in the MVC layer, which calls the service layer, which initiates a transaction and calls the DAO layer, which calls the ORM, which stores the object in the database. It might be even worse - you may need to convert the bound request data from a form bean to a Data Transfer Object, both of which could be separate from the domain object. Normalizing this operation would mean binding request data to a domain object, which then stores itself.<br /><br />So, building an ActiveRecord base class in Java means that at least the following methods must be implemented:<br /><pre><br />public class ActiveRecord<T> {<br />public static <T> T load(Long id) { .. }<br />public void store() { .. }<br />public void delete() { .. }<br />public static <T> List<T> findAll() { .. }<br />}<br /></pre><br />Derived classes will add all sorts of operations, notably a number of specialized finders with similar signatures to findAll().<br /><br />In order to be able to access non-domain services we need a way to access external services, preferably through Dependency Injection. It's also mandatory that we are able to isolate the domain object for test purposes, and to be able to mock or stub dependencies, all of which is enabled by using DI.<br /><br />It's also natural to use a full-fledged ORM - Java now has a standard API for that (JPA), and there are a number of different implementations available: Hibernate, TopLink, OpenJPA etc. There's also JDO, iBatis and of course you could also use plain JDBC if you really have to.<br /><br />I've been using (surprise, surprise!) Hibernate as ORM tool, and Spring for DI and transaction demarcation, all of which are using annotations and build-time weaving using AspectJ and AJDT. Dependency injection of service beans into domain objects is taken care of by the @Configurable annotation, and transaction demarcation by the the @Transactional annotation, so those problems are very cleanly solved. <span style="font-weight: bold;"><br /><br />However</span>: static methods, such as loaders and finders, don't seem to be woven by transactional advice, at least not when using compile-time weaving. I need to investigate this further, but I believe it might be caused by the fact that a "this" joinpoint is being used <a href="http://fisheye1.cenqua.com/browse/springframework/spring/aspectj/src/org/springframework/transaction/aspectj/AnnotationTransactionAspect.aj?r=1.7">here</a>. Workarounds include writing transactional advice manually, using <a href="http://static.springframework.org/spring/docs/2.0.x/api/org/springframework/transaction/support/TransactionTemplate.html">TransactionTemplate</a> for example, or following the mixed AR/Repository pattern suggested below. The best solution would be to have an aspect that's able to read transaction attributes from annotations even in a static context.<br /><br />The first roadblock is how to gain access to the ORM in a static context, the loader and finder methods. We need a reference to the unit-of-work (session) provider - the SessionFactory in this case - in order to create or obtain the current session for performing data operations. Since we don't have an instance of AR or a derived class, we can't access any injected SF reference.<br /><br />I've thought long and hard about this, and tried a number of different approaches, but the way I see it, you basically have to give up either static loaders/finders or give up dependency injection. Any way you look at it, you will need some sort of static handle to the SF - you might make the SF member of the AR class static, or you could use some variation of <a href="http://www.hibernate.org/hib_docs/v3/reference/en/html/tutorial.html#tutorial-firstapp-helpers">SessionFactoryUtil</a>, or maybe use some sort of lightweight holder object that's instantiated, injected and finally discarded as part of the static operation. The problem with static references, and the ServiceLocator pattern, is that you can't isolate objects completely - there can be only one implementation per class loader at a time, and all instances of a class with a static reference must share the same implementation. This makes testing harder and less robust, compared to a pure DI environment: you must make sure to "reset" the service locator reference after each test, even in case of failures, so you'll end up with a number of try - finally blocks everywhere, and you can't run tests in parallell since you can't guarantee what implementation the factory will return. (Crazy) Bob Lee talks a little about that in <a href="http://video.google.com/videoplay?docid=6068447410873108038">this Guice presentation</a>.<br /><br />So, as far as I can tell, you will need to give up DI, or at least mix DI and ServiceLocator in your application, if you want "pure" ActiveRecord. I'll be very interested if anyone can show a way to use AR and DI together, though :-). For now, I prefer a mix between AR and Repository, but more about that in a little while. If you decide to go for pure AR by using a static SF reference, your next problem will be how to tell the ORM which class to load, without adding redundant data in derived classes, such as overriding load() or keeping a static class member pointing to its own class.<br /><br />When performing a load, you will need to know what class, or sometimes what table, to load the data from, in addition to the supplied identifier property. Optimally, the implementation of load() exists in the top class ActiveRecord only, and should return a correctly typed object. In short, we want to use the API like this:<br /><pre><br />Customer c = Customer.load(1);<br />Item item = Item.load(125);<br /></pre><br />where Customer and Item both inherit ActiveRecord.<br /><br />Typing is taken care of by generics, as you can see a few paragraphs earlier. But since it's a static method, there's no "this" to ask for the current class, so finding the current class to feed to Session.load(id, clazz) is harder. There is no API access point in Java, and I've fiddled with various reflection hacks against sun.reflect.Reflections for example, to peek at the call stack, but to no avail. I did however find a way by using the "call" joinpoint in AspectJ and the following construct:<br /><pre><br />/*<br />* Keep the traditional load() signature as access point.<br />*/<br />public static <T> T load(Long id) {<br />throw new RuntimeException("This body should never be reached, weaving has not been performed");<br />}<br /><br />/*<br />* This method performs an actual load.<br />*/<br />protected static <T> T doLoad(Long id, Class clazz) {<br />return (T) getSession().load(id, clazz);<br />}<br /><br />/*<br />* This inner class aspect intercepts the call to load, and inspects the join point<br />* to determine what class the static call was made on, and reroutes the call to<br />* doLoad() with the correct class parameter.<br />*/<br />@Aspect<br />protected static class ClassIdentifier {<br /><br />@Before("call (* load(Long)) && args(id)")<br />public T interceptLoad(ProceedingJoinPoint pjp, Long id) {<br /> Class clazz = pjp.getSignature().getDeclaringType();<br /> return ActiveRecord.doLoad(id, clazz);<br />}<br /><br />}<br /></pre><br />So, if you are prepared to give up reliable replacement of the ORM interface reference, ActiveRecord is within reach. In fact, you might find that you rarely or never mock or stub something like the SessionFactory, but instead simply use a dedicated data source for testing which is loaded with test data. That's perfectly reasonable imo, if you work directly against the ORM API, but it gets a bit more complicated if you keep your own DAO layer around, tested separately from the domain object, and inject that into your domain objects instead.<br /><br />If you want to go for pure DI, I would argue that it's reasonable to split CRUD operations between the domain objects and a shared "read only"-repository. Operations that work on an actual instance, such as user.store() or item.delete() (non-static by nature), are placed in the domain objects. But operations that result in one or more instances are retrieved according to various criteria, loaders and finders, are placed on a separate service. Those are the same methods that would be static in ActiveRecord.<br /><br />This approach is DI-compliant, because each domain object instance has its own non-static reference to the ORM, and the Repository service also is injected with an ORM reference. The repository can be regarded as a third party, where you go to retrieve instances of domain objects. By clever use of generics, the amount of code can be kept low, and a single @Transactional annotation at class level on the implementation marks every method for execution in a read-only transaction.<br /><pre><br />interface Repository {<br />// Typing on the methods instead of the interface<br />// allows us to share this interface across the domain model<br /><T> T load(Long id, Class clazz);<br /><T> List<T> findAll(Class clazz);<br /><br />// Various specific finders are added as they are needed.<br />Customer findByUsername(String username);<br />List<Item> findDeliveredItems(); <br /><br />// Some kind of generic query-object method might be added too<br />}<br /></pre><br />This distinction between instance-tied domain logic versus third-party is then extrapolated throughout the application.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com2tag:blogger.com,1999:blog-32660782.post-19336875474618265042007-06-27T10:53:00.000+02:002007-07-13T09:23:39.025+02:00Weird but useful generics trickI stumbled upon the following piece of code while programmatically creating AspectJ proxies with Spring:<br /><pre><br />// create a factory that can generate a proxy for the given target object<br />AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);<br /><br />// add an aspect, the class must be an @AspectJ aspect<br />// you can call this as many times as you need with different aspects<br />factory.addAspect(SecurityManager.class);<br /><br />// you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspect<br />factory.addAspect(usageTracker);<br /><br />// now get the proxy object...<br />MyInterfaceType proxy = factory.getProxy();<br /></pre><br />(Pasted from <a href="http://www.springframework.org/docs/reference/aop.html#aop-aspectj-programmatic">here</a>)<br /><br />If you look carefully, you'll notice that A) the AspectJProxyFactory class is <span style="font-style: italic;">not</span> parameterized, and B) the proxy creation is assigned to a MyInterfaceType <span style="font-style: italic;">without a cast</span>. I found this to be rather confusing, and took a quick peek at the <a href="http://fisheye1.cenqua.com/browse/springframework/spring/tiger/src/org/springframework/aop/aspectj/annotation/AspectJProxyFactory.java?r=1.6">AspectJProxyFactory source</a>:<br /><pre><br />public class AspectJProxyFactory extends ProxyCreatorSupport {<br /><br />[...] // Stuff<br /><br />public <T> T getProxy() {<br /> return (T) createAopProxy().getProxy();<br />}<br /><br />}<br /></pre>The return type is inferred by the assignment, regardless of the (lack of) type on the owning class, which effectively looks and feels like dynamic and static typing all at the same time!<br /><br />Kind of weird, but might be useful. I, for one, was not aware of this technique.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com1tag:blogger.com,1999:blog-32660782.post-73530168426385492692007-05-30T20:50:00.000+02:002007-05-31T15:41:40.647+02:00Making JDOM run fasterIt's time for another cool AspectJ hack here on RMA, once again leveraging Codehaus' excellent <a href="http://mojo.codehaus.org/aspectj-maven-plugin/">Maven plugin</a>. Still working on the same project as the last post, we've decided to move from standard JAXP to <a href="http://www.jdom.org/">JDOM</a> for programmatic XML handling (the JDOM api is so much nicer to work with it's not even funny). As the returning reader may recall, the model in the MVC part of our application basically consists of a number of XML documents, and the view rendering technology is <a href="http://www.freemarker.org/">Freemarker</a>. Freemarker is supposed to be able to handle both JAXP, JDOM and dom4j, but a closer look will reveal that it's more or less only JAXP that's up to date, the other wrappers are deprecated and don't work correctly anymore, which is a shame.<br /><br />Using JDOM, there is a simple workaround however: the <a href="http://www.jdom.org/docs/apidocs/org/jdom/output/DOMOutputter.html">DOMOutputter</a>, which is capable of converting an org.jdom.Document to an org.w3c.dom.Document which can then be handed to Freemarker. There is a speed penalty of course, and when profiling we found that almost all the time is spent in this method, in <a href="http://www.jdom.org/docs/apidocs/org/jdom/adapters/JAXPDOMAdapter.html">JAXPDOMAdapter</a>:<br /><pre><br />public Document createDocument() throws JDOMException {<br /><br />try {<br /> // We need DOM Level 2 and thus JAXP 1.1.<br /> // If JAXP 1.0 is all that's available then we error out.<br /> Class.forName("javax.xml.transform.Transformer");<br /><br /> // Try JAXP 1.1 calls to build the document<br /> Class factoryClass =<br /> Class.forName("javax.xml.parsers.DocumentBuilderFactory");<br /><br /> // factory = DocumentBuilderFactory.newInstance();<br /> Method newParserInstance =<br /> factoryClass.getMethod("newInstance", null);<br /> Object factory = newParserInstance.invoke(null, null);<br /><br /> // jaxpParser = factory.newDocumentBuilder();<br /> Method newDocBuilder =<br /> factoryClass.getMethod("newDocumentBuilder", null);<br /> Object jaxpParser = newDocBuilder.invoke(factory, null);<br /><br /> // domDoc = jaxpParser.newDocument();<br /> Class parserClass = jaxpParser.getClass();<br /> Method newDoc = parserClass.getMethod("newDocument", null);<br /> org.w3c.dom.Document domDoc =<br /> (org.w3c.dom.Document) newDoc.invoke(jaxpParser, null);<br /><br /> return domDoc;<br /> } catch (Exception e) {<br /> throw new JDOMException("Reflection failed while creating new JAXP document", e);<br /></pre> <br />You're probably wondering why the hell they're using this much reflection just to create an empty Document (we did, anyway). It avoids compile-time dependencies on certain javax.xml classes, but it sure isn't designed with high performance in mind!<br /><br />In addition to that, the call trace from DOMOuputter to JAXPDOMAdapter contains private methods, so you can't simply inherit and override with your own implementation. So, what now? AspectJ to the rescue, of course!<br /><br />What we realy want to do is replace this implementation with one that performs the sam thing but statically, so we wrote this around advice (DocBuilder creation omitted for brevity):<br /><pre><br />@Aspect<br />public class FastDOMDocumentCreator {<br /><br />@Around("execution (* org.jdom.output.DOMOutputter.createDOMDocument(..))")<br />public Object createDOMDocument(ProceedingJoinPoint pjp) throws Throwable {<br /> return docBuilder.newDocument();<br />}<br /><br />}<br /></pre><br />This is a lot faster than the default way. But how do we perform weaving of JDOM classes, that are in an external jar? Actually, it's really simple: just place this snippet in the AspectJ plugin section in the pom-xml (details <a href="http://mojo.codehaus.org/aspectj-maven-plugin/weaveJars.html">here</a>):<br /><pre><br /><configuration><br /><weaveDependencies><br /> <weaveDependency><br /> <groupId>jdom</groupId><br /> <artifactId>jdom</artifactId><br /></weaveDependency><br /></configuration><br /></pre><br />If you're using <a href="http://www.eclipse.org/ajdt/">AJDT</a> (which I highly recommend), make sure you add the JDOM jar to the inpath in the configuration dialog, that way this weaving runs on the fly just like regular compilation. On a sidenote, AJDT has some really cool features such as contextual information on "weaves" and "weaved by". It supports annotation-style aspects too.<br /><br />The weaved jar is exploded under the output directory, with the new weaved classes instead of the vanilla ones. This trick pushed the JDOM-to-JAXP document converstion from the top way down on the hotspot list, with a fairly small amount of work.<br /><br /><span style="font-style: italic;">Update: While examining the situation a bit closer, we found that in fact it's possible to accomplish the same thing without having to weave the JAXPDOMAdapter. Extending AbstractDOMAdapter using the same code as in the aspect, and passing the new class' name to the DOMOutputter constructor will work too. Nevertheless, the technique is still interesting and may be applicable (as the only solution) somwhere else. </span><div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com1tag:blogger.com,1999:blog-32660782.post-33759787717355297552007-05-16T14:16:00.000+02:002007-05-16T15:36:36.006+02:00Clean solutions are the bestDon't you love it when things just seem to fit together exactly as you want them to? It doesn't happen all too often, but when it does, you just want to <span style="font-style: italic;">blog at the top of your lungs</span>...<br /><br />I'm working on a project where we communicate with an index server over HTTP, and recieve the query results in XML format. It's a data source, which basically boils down to this interface - the DocumentSource:<br /><pre><br />Document retrieveDocument( URL url );<br /></pre><br />The contract of this component is that if the document retrieval succeeds, we get a parsed and ready org.w3c.Document back, and in all other cases it throws some kind of exception. Handling exceptions is done in another part of the application, so don't worry about that right now.<br /><br />Now, a failure can be either uncontrolled, for example if the index application hits a bug, if there's a network problem, the server might be on fire and so on. It could also be a problem with our implementation of of the afromentioned interface (not likely). In either case, the component will throw an exception to the caller, possibly wrapped in some way.<br /><br />But a failure can also be controlled, which means that the index server returns a valid XML error message along with HTTP status 200 (OK), if the query is invalid in any way. This is also considered an error, and we handle in like this:<br /><pre><br />if (isErrorResult(document)) {<br /> throw new IndexErrorException(method, document);<br />}<br /></pre><br />The method variable is the <a href="http://jakarta.apache.org/commons/httpclient/methods.html">HttpMethod</a> that we tried to execute, containing host, port and query information, and document is the parsed XML response from the index, in this case an error message.<br /><br />What we want to do now is log this error, as transparently as possible. The snippet above expresses that we're not really interested in handling the error in detail, we just leave it at <span style="font-style: italic;">"ok, there was an error, so let's throw an exception. Here's all the information I have on why and where the error occurred"</span>.<br /><br />That's all very well, but we still have to inspect the XML error message to present the error in a readable way. The best way to do that is of course the message property of the exception.<br /><br />Here's where another aspect of our application comes in: we use <a href="http://freemarker.org/docs/xgui_imperative_learn.html">Freemarker</a> to build views (HTML and others) using the XML data we retrieve from the DocumentSource interface shown above. And since the error message is also XML, and we want to build a kind of view - a String - why not use the same approach here? That way we won't have to deal with cumbersome Java DOM apis, and we have maximum power to extract and format the error message the way we want. Sounds like a good idea.<br /><br />It's also the case that we've abstracted away a lot of the fuss around the template engine, as well as the fact that we're using Freemarker, behind this very simple TemplateService interface:<br /><pre><br />String mergeTemplateIntoString( String templateName, Map model );<br /></pre><br />This service is a component, a Spring bean, in our application. It would be great if this service could be used to render the logged string from the document and the HTTP method, but in that case we have to supply the newly instantiated exception with a reference from the context. Sure, this could be accomplished by holding an (otherwise useless) reference in the DocumentSource implementation that is passed along the exception, but I prefer it when things <a href="http://www.apple.com/getamac/works.html">Just Work</a>.<br /><br />Enter <a href="http://www.springframework.org/docs/reference/aop.html#aop-atconfigurable">@Configurable</a> and compile-time AspectJ weaving! We slap on an a TemplateService setter and an annotation (with autowire=Autowire.BY_NAME to avoid the need for a boilerplate bean definition) on the exception class. We're using AspectJ aspects for various other tasks already, and weaving is done at compile-time to avoid the <a href="http://www.springframework.org/docs/reference/aop.html#aop-understanding-aop-proxies">proxy problem</a>. The <a href="http://mojo.codehaus.org/aspectj-maven-plugin/">Codehaus Maven plugin</a> works great!<br /><br />In order to weave the Spring AnnotationBeanConfigurerAspect, we add <a href="http://mojo.codehaus.org/aspectj-maven-plugin/libraryJars.html">the following</a> to pom.xml:<br /><pre><br /><aspectLibraries><br /> <aspectLibrary><br /> <groupId>org.springframework</groupId><br /> <artifactId>spring-aspects</artifactId><br /> </aspectLibrary><br /></aspectLibraries><br /></pre><br />And the aspect must be made aware of the Spring context (in any application context file):<br /><pre><br /><aop:spring-configured /><br /></pre>So from here on, the exception message may be rendered like this:<br /><pre><br />@Override<br />public String getMessage( ) {<br /> return templateService.mergeTemplateIntoString( "error", model );<br />}<br /></pre><br />where "error" is the name of a template, and the model contains the XML document and some other stuff. The error message is now ready to be read and logged, for example in an @AfterThrowing aspect, but that's another story.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com1tag:blogger.com,1999:blog-32660782.post-44959308315915419562007-05-06T19:13:00.000+02:002007-05-06T19:31:39.826+02:00Integrating Struts 1 and SpringIf you want to manage Struts 1 actions with Spring, for example to inject dependencies at runtime, you are going to need a bridging context that essentially duplicates every single action as a bean. (details are <a href="http://static.springframework.org/spring/docs/2.0.x/reference/webintegration.html#struts">here</a>). If you're working on an existing application with lots of actions, writing the bridge context XML will be a pain, so here's a quick Groovy hack that I came up with to generate the Spring context file from the Struts config file:<br /><pre><br />import groovy.xml.MarkupBuilder<br /><br />def strutsConfig = new File("path/to/struts-config.xml")<br /><br />def fromXml = new XmlParser().parse(strutsConfig)<br /><br />def writer = new StringWriter()<br />def toXml = new MarkupBuilder(writer)<br /><br />toXml.beans(<br /> xmlns:"http://www.springframework.org/schema/beans",<br /> "xmlns:xsi":"http://www.w3.org/2001/XMLSchema-instance",<br /> "xsi:schemaLocation":"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd",<br /> "default-autowire":"byName") {<br /> fromXml."action-mappings".action.each {<br /> bean(name:it["@path"],class:it["@type"])<br /> }<br /> }<br />}<br /><br />// This simply dumps the XML to stdout, you could of course write to an actual file as well<br />println writer<br /></pre><br />This little snippet creates a <bean name="/foo" class="bar.Baz"/> for every <action path="/foo" type="bar.Baz"/> element in the struts-config.xml file, using a few neat Groovy tricks (<a href="http://groovy.codehaus.org/GPath">GPath</a>, <a href="http://groovy.codehaus.org/Closures">closures</a>, <a href="http://groovy.codehaus.org/GroovyMarkup">GroovyMarkup</a>).<br /><br />Still, you could do better. You could use a hook such as <a href="http://www.springframework.org/docs/api/org/springframework/beans/factory/config/BeanFactoryPostProcessor.html">BeanFactoryPostProcessor </a>that parses the Struts config and dynamically creates beans corresponding to the actions, or even write an XSD that allows Spring to interpret the Struts config file as a context file directly. There's an <a href="http://springide.org/blog/2007/04/12/most-useless-custom-namespace-ever/">entry </a>in the SpringIDE blog that shows how you could write a namespace that works in German, so for example <bean/> becomes <bohne/> and so on. That principle might be applicable to this context too.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0tag:blogger.com,1999:blog-32660782.post-31276342949665061692007-04-25T13:16:00.000+02:002007-04-25T13:42:11.557+02:00Bandwidth saverI never miss an opportunity to push for <a href="http://freemarker.org/">Freemarker,</a> the most powerful template engine in the world, so here's a quick tip on how to strip all whitespace from your HTML pages:<br /><pre><br /><#assign out><br /><#compress><br /><span class="doctype">!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"></span><br /><HTML><br /><HEAD><br /><br /> // Your page here<br /><br /></BODY><br /></HTML><br /></#compress><br /></#assign><br />${out?replace('\n','')}<br /></pre><br /><br />It assigns the <a href="http://freemarker.org/docs/ref_directive_compress.html">compressed</a> body of the outer <a href="http://freemarker.org/docs/ref_directive_assign.html">assign</a> macro to the variable "out", and the finally writes the value of "out" with all newlines <a href="http://freemarker.org/docs/ref_builtins_string.html#ref_builtin_replace">stripped.</a><br /><br />The entire page is now a singe line of HTML - a developer's nightmare, but a nice a bandwidth saver.<br /><br />Combined with <a href="http://www.opensymphony.com/sitemesh/">Sitemesh,</a> another one of my favorite tools, this is easily and transparently applied to all your pages.<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0tag:blogger.com,1999:blog-32660782.post-27029528193436286212007-04-17T22:18:00.000+02:002007-04-17T22:34:03.442+02:00A quick peek at SpringIDE 2.0There are a number of cool new features coming in <a href="http://springide.org/blog/">SpringIDE</a> 2.0, currently at milestone 3. One example is the new <a href="http://springide.org/project/wiki/SpringExplorer">SpringExplorer</a>, which is a comprehensive tree view of a Spring context, another is content assist for <a href="http://springide.org/blog/2007/04/05/adding-support-for-custom-namespaces">custom namespaces</a>.<br /><br />But the by far neatest new feature is the ability to <a href="http://springide.org/blog/2007/01/27/insights-of-spring-ides-aop-support/">evaluate AspectJ expressions</a> in XML on the fly, <span style="font-style: italic;">and</span> displaying what beans will be advised! Take a close look at <a href="http://springide.org/project/attachment/wiki/SpringideAopShowcase/spring_ide_aop_3.png?format=raw">this screenshot</a>...an <aop:advisor> element is highlighted on the left, with an AspectJ pointcut expression. On the right, you can see what methods will be advised (the "advises" arrow). A godsend!<div class="blogger-post-footer"><script type="text/javascript"><!--
google_ad_client = "pub-2421132329851518";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel = "";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "0000FF";
google_color_text = "000000";
google_color_url = "008000";
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></div>Peter Backlundhttp://www.blogger.com/profile/17377084852220577629noreply@blogger.com0