<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7818858158873072140</id><updated>2024-10-24T22:42:48.675+00:00</updated><category term="Grails"/><category term="Testing"/><category term="Groovy"/><category term="Rails"/><category term="SwingBuilder"/><category term="AntBuilder"/><category term="SVN"/><category term="SyntaxHighlighter"/><category term="System Tray"/><category term="TDD"/><title type='text'>Marcin&#39;s Dev Info</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://marcinfo.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://marcinfo.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Marcin Domanski</name><uri>http://www.blogger.com/profile/00857288385499196720</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://marcin.doman.googlepages.com/marcin.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>13</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7818858158873072140.post-3695790363466846729</id><published>2008-01-15T00:08:00.000+00:00</published><updated>2008-01-15T01:11:33.874+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="SyntaxHighlighter"/><title type='text'>Integrating SyntaxHighlighter with Blogger</title><content type='html'>I&#39;ve just found a quick and easy way of integrating &lt;a href=&quot;http://code.google.com/p/syntaxhighlighter/&quot;&gt;SyntaxHighlighter&lt;/a&gt; with Blogger. Go and check &lt;a href=&quot;http://developertips.blogspot.com/2007/08/syntaxhighlighter-on-blogger.html&quot;&gt;this&lt;/a&gt;  blog entry and start posting coloured code snippets!&lt;br /&gt;&lt;br /&gt;Two things to note though. If your theme uses background images for &amp;lt;li&amp;gt; elements, you will have to apply this patch to your CSS:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;css&quot;&gt;&lt;br /&gt;div.dp-highlighter li {&lt;br /&gt;  background-image: none;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Otherwise each line of your code snippets will start with a background image as it is coded with an &amp;lt;li&amp;gt; element. &lt;br /&gt;&lt;br /&gt;Finally, if you copy and paste the whole javascript from step 2 your syntax highlighting will only support the following languages:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Cpp&lt;/li&gt;&lt;li&gt;CShapr&lt;/li&gt;&lt;li&gt;CSS&lt;/li&gt;&lt;li&gt;Java&lt;/li&gt;&lt;li&gt;JScript&lt;/li&gt;&lt;li&gt;Sql&lt;/li&gt;&lt;li&gt;Xml&lt;/li&gt;&lt;/ul&gt;Syntax Highlighter supports more, for example Ruby, Delphi or VB. If you want to use any of them, you can do this by adding an extra line like this (for Ruby):&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;html&quot;&gt;&lt;br /&gt;&amp;lt;script src=&#39;http://[YOUR HOST]/shBrushRuby.js&#39; type=&#39;text/javascript&#39;/&amp;gt; &lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://marcinfo.blogspot.com/feeds/3695790363466846729/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/7818858158873072140/3695790363466846729' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/3695790363466846729'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/3695790363466846729'/><link rel='alternate' type='text/html' href='http://marcinfo.blogspot.com/2008/01/integrating-syntaxhighlighter-with.html' title='Integrating SyntaxHighlighter with Blogger'/><author><name>Marcin Domanski</name><uri>http://www.blogger.com/profile/00857288385499196720</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://marcin.doman.googlepages.com/marcin.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7818858158873072140.post-5077175616355281597</id><published>2007-11-27T21:10:00.000+00:00</published><updated>2007-11-27T21:59:43.422+00:00</updated><title type='text'>Firefox 3 Beta 1 passes the Second Acid Test</title><content type='html'>I was quite excited when Firefox 3 Beta 1 was released a few days ago - for at least two reasons. &lt;br /&gt;&lt;br /&gt;The first thing I thought about the other day was the memory - I was hoping I will read something about it in the &lt;a href=&quot;http://www.mozilla.com/en-US/firefox/3.0b1/releasenotes/&quot;&gt;release notes&lt;/a&gt;, as the second version of the browser can consume lots of MB of RAM. By the way, I am not sure what the world record is, but here you can see &lt;a href=&quot;http://marcinfo.blogspot.com/2007/07/firefox-at-18-gb-of-ram.html&quot;&gt;Firefox at 1.8 GB&lt;/a&gt;. Therefore, it was really nice to find out that &quot;over 300 individual memory leaks have been plugged&quot;!&lt;br /&gt;&lt;br /&gt;The first thing I did though after installing FF 3 Beta 1 was to see how it does with &lt;a href=&quot;http://www.webstandards.org/files/acid2/test.html#top&quot;&gt;the second acid test&lt;/a&gt; and whether it can be as perfect as Opera is. And to my surprise, Firefox is actually perfect! Finally, it renders the correct &#39;face&#39;:&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://www.webstandards.org/files/acid2/reference.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 168px;&quot; src=&quot;http://www.webstandards.org/files/acid2/reference.png&quot; border=&quot;0&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;br /&gt;Just after that test I read about rendering improvements in CSS in the release notes. Well done the Firefox dev team! For me as a web developer that is a really good news.</content><link rel='replies' type='application/atom+xml' href='http://marcinfo.blogspot.com/feeds/5077175616355281597/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/7818858158873072140/5077175616355281597' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/5077175616355281597'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/5077175616355281597'/><link rel='alternate' type='text/html' href='http://marcinfo.blogspot.com/2007/11/firefox-3-beta-1-passes-second-acid.html' title='Firefox 3 Beta 1 passes the Second Acid Test'/><author><name>Marcin Domanski</name><uri>http://www.blogger.com/profile/00857288385499196720</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://marcin.doman.googlepages.com/marcin.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7818858158873072140.post-8795219164528058858</id><published>2007-11-23T17:25:00.000+00:00</published><updated>2007-11-23T17:27:27.586+00:00</updated><title type='text'>How Many HTML Elements Can You Name in 5 Minutes?</title><content type='html'>&lt;a id=&quot;mingle2_badge&quot; href=&quot;http://www.justsayhi.com/bb/html_quiz&quot; style=&quot;display: block; background:url(http://assets.justsayhi.com/badges/530/602/html_elements.9x9iq2ekz6.jpg) no-repeat top left; height: 147px; width: 335px; text-decoration: none; color: #fff;&quot;&gt;&lt;strong id=&quot;mingle2_badge_score&quot; style=&quot;display: block; padding-left: 125px; padding-top: 44px; font-weight: normal; font-family: Times New Roman, Arial; font-size: 45px;&quot;&gt;47&lt;/strong&gt;&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://marcinfo.blogspot.com/feeds/8795219164528058858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/7818858158873072140/8795219164528058858' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/8795219164528058858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/8795219164528058858'/><link rel='alternate' type='text/html' href='http://marcinfo.blogspot.com/2007/11/how-many-html-elements-can-you-name-in.html' title='How Many HTML Elements Can You Name in 5 Minutes?'/><author><name>Marcin Domanski</name><uri>http://www.blogger.com/profile/00857288385499196720</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://marcin.doman.googlepages.com/marcin.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7818858158873072140.post-4625210421522510379</id><published>2007-09-17T22:14:00.000+00:00</published><updated>2008-12-08T23:03:25.160+00:00</updated><title type='text'>NetBeans 6.0 Beta 1 and custom Tomcat</title><content type='html'>I&#39;ve just installed the first beta version of NetBeans 6.0, which was released today. Just out of curiosity, to see how it looks, performs and whether or not there is something better than Eclipse has. It was the full pack including Java SE, Web &amp; Java EE, Mobility, SOA, Ruby, C/C++ and GlassFish. I decided not to install Tomcat as I had it already. To be honest, I got surprised before I even started using it ...&lt;br /&gt;&lt;br /&gt;The installation took a long while, about 3, maybe 5 minutes. It was the full installation, so fair enough, though still don&#39;t understand why it has to take so long. Eclipse can be installed just by unzipping it to a directory of your choice and that&#39;s it. +1 for Eclipse.&lt;br /&gt;&lt;br /&gt;NetBeans 6.0 requires about 400 MB for the full installation, plus 140 for GlassFish, if you choose to install it. Quite a lot, however my Eclipse 3.3 takes about the same size with the latest patches and the following plug-ins: Subclipse, Spring IDE, Tomcat , Maven 2, Web Tools and PMD.&lt;br /&gt;&lt;br /&gt;The biggest surprise, however, was Tomcat integration. As I mentioned before, I chose to use my own Tomcat, so the first thing I did after startup was to add it to the servers. Here I got an odd warning saying that I might have problems running Tomcat as the startup scripts are missing (due to the windows installation which does not include these). The &#39;add server&#39; wizard closed down, Tomcat was added successfully. When I tried to start it I understood what the warning was all about:&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_bVQlWTWjKt24hqrtIJb6AQ28ZKRoJfCQ_EJzTR-1OkQZhQKopChvo9egBxh-0P8D-Tm61Gw2DdIaqvyXhciYi_cGB3KriZAiJ-y3UFMKanUAzCrxcN49evbwff3dp5_eLo2NYEamXR0/s1600-h/missing_catalina.bat.png&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_bVQlWTWjKt24hqrtIJb6AQ28ZKRoJfCQ_EJzTR-1OkQZhQKopChvo9egBxh-0P8D-Tm61Gw2DdIaqvyXhciYi_cGB3KriZAiJ-y3UFMKanUAzCrxcN49evbwff3dp5_eLo2NYEamXR0/s320/missing_catalina.bat.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5111301580521198290&quot; /&gt;&lt;/a&gt;&lt;br /&gt;You must be joking, I thought. That&#39;s ridiculous! Why the Tomcat plug-in in Eclipse can start any Tomcat since version 3.3 (including my Windows-based) without any startup scripts, and NetBeans simply can not?! I don&#39;t believe the NetBeans team don&#39;t know that they can use bootstrap.jar instead. And guess what, all the instructions how to use it are in catalina.bat!&lt;br /&gt;&lt;br /&gt;After that, I decided not to play with NetBeans 6.0 Beta 1 any more. And if there is anything I am going to uninstall is definitely not Tomcat, but NetBeans.&lt;br /&gt;&lt;br /&gt;Oh, and one more thing. Does anyone know why NetBeans reads my floppy disk on startup and exit? Does it search for the missing catalina.bat??</content><link rel='replies' type='application/atom+xml' href='http://marcinfo.blogspot.com/feeds/4625210421522510379/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/7818858158873072140/4625210421522510379' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/4625210421522510379'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/4625210421522510379'/><link rel='alternate' type='text/html' href='http://marcinfo.blogspot.com/2007/09/netbeans-60-beta-1-and-custom-tomcat.html' title='NetBeans 6.0 Beta 1 and custom Tomcat'/><author><name>Marcin Domanski</name><uri>http://www.blogger.com/profile/00857288385499196720</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://marcin.doman.googlepages.com/marcin.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_bVQlWTWjKt24hqrtIJb6AQ28ZKRoJfCQ_EJzTR-1OkQZhQKopChvo9egBxh-0P8D-Tm61Gw2DdIaqvyXhciYi_cGB3KriZAiJ-y3UFMKanUAzCrxcN49evbwff3dp5_eLo2NYEamXR0/s72-c/missing_catalina.bat.png" height="72" width="72"/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7818858158873072140.post-5236494173810000856</id><published>2007-08-16T22:25:00.000+00:00</published><updated>2007-08-17T11:11:22.318+00:00</updated><title type='text'>Learning GigaSpaces at Financial Times</title><content type='html'>If someone had told me that I was going to work at Financial Times at one day, I would never have believed...&lt;br&gt;&lt;br&gt;It is happening now, I&#39;ve just joined a project for the FT where some of the work is going to be done at the client&#39;s office! The funny thing is that I have been passing FT&#39;s building on my bicycle for the last one year or so and have never even thought that I might work at that place.&lt;br&gt;&lt;br&gt;Today I spent my first day at FT learning &lt;a title=&quot;GigaSpaces&quot; target=&quot;_blank&quot; href=&quot;http://www.gigaspaces.com&quot; id=&quot;dxiy&quot;&gt;GigaSpaces&lt;/a&gt; and &lt;a title=&quot;OpenSpaces&quot; target=&quot;_blank&quot; href=&quot;http://www.gigaspaces.com/wiki/display/GS6/Open+Spaces+Overview&quot; id=&quot;dv9c&quot;&gt;OpenSpaces&lt;/a&gt;. Yet another stuff I have never thought I might need to learn in my career! Especially that I am slowly thinking of doing RoR development (after a couple of years of Java). Now it looks like I will be doing Java for another couple of months, if not for a year. Even so, I think it is worth having GigaSpaces in my CV! &lt;br /&gt;&lt;br /&gt;Anyway, I am not an expert here, but it sounds like a very promising solution for a highly performant, scalable and distributed application. Something the FT need!  &lt;br&gt;&lt;br&gt;</content><link rel='replies' type='application/atom+xml' href='http://marcinfo.blogspot.com/feeds/5236494173810000856/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/7818858158873072140/5236494173810000856' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/5236494173810000856'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/5236494173810000856'/><link rel='alternate' type='text/html' href='http://marcinfo.blogspot.com/2007/08/learning-gigaspaces-at-financial-times.html' title='Learning GigaSpaces at Financial Times'/><author><name>Marcin Domanski</name><uri>http://www.blogger.com/profile/00857288385499196720</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://marcin.doman.googlepages.com/marcin.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7818858158873072140.post-3411348715217042223</id><published>2007-07-15T22:07:00.000+00:00</published><updated>2013-06-19T07:47:22.104+00:00</updated><title type='text'>Firefox at 1.8 GB of RAM</title><content type='html'>One of the reasons why I hate Firefox is the memory that it uses. It needs about 100MB - 200MB for 10 tabs, or even more if one of these is Gmail or Google Reader. If you open more tabs and switch quickly between them etc, the browser just slows down and needs a restart. &lt;br /&gt;
&lt;br /&gt;
But this is actually nothing compared to what Firefox managed to do today. I was navigating Google maps with about 10 other tabs opened at the same time, when this happend:&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiL7IqMHN4IXqyTEv8Hykapgcvzd4JAhsjJQdPmoun0QrneaXYutgFrL6xMpwvpCSm5B86cELMympGuo3ZbkUf2EBSYInhvZlh5d9GH_puTvTpjIbDm2D-IDhnVu5x9tgpIpFUAI6eqDeA/s1600-h/firefox+at+1.8+GB.PNG&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; id=&quot;BLOGGER_PHOTO_ID_5087553581142396418&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiL7IqMHN4IXqyTEv8Hykapgcvzd4JAhsjJQdPmoun0QrneaXYutgFrL6xMpwvpCSm5B86cELMympGuo3ZbkUf2EBSYInhvZlh5d9GH_puTvTpjIbDm2D-IDhnVu5x9tgpIpFUAI6eqDeA/s320/firefox+at+1.8+GB.PNG&quot; style=&quot;cursor: hand; cursor: pointer; display: block; margin: 0px auto 10px; text-align: center;&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
My box has &#39;only&#39; 2 gigs of RAM. I guess Firefox would have used even more if there was more to use ...&lt;br /&gt;
&lt;br /&gt;
Luckily, I didn&#39;t have to restart my box, just the browser ;-)</content><link rel='replies' type='application/atom+xml' href='http://marcinfo.blogspot.com/feeds/3411348715217042223/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/7818858158873072140/3411348715217042223' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/3411348715217042223'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/3411348715217042223'/><link rel='alternate' type='text/html' href='http://marcinfo.blogspot.com/2007/07/firefox-at-18-gb-of-ram.html' title='Firefox at 1.8 GB of RAM'/><author><name>Marcin Domanski</name><uri>http://www.blogger.com/profile/00857288385499196720</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://marcin.doman.googlepages.com/marcin.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiL7IqMHN4IXqyTEv8Hykapgcvzd4JAhsjJQdPmoun0QrneaXYutgFrL6xMpwvpCSm5B86cELMympGuo3ZbkUf2EBSYInhvZlh5d9GH_puTvTpjIbDm2D-IDhnVu5x9tgpIpFUAI6eqDeA/s72-c/firefox+at+1.8+GB.PNG" height="72" width="72"/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7818858158873072140.post-5382818263807243512</id><published>2007-06-03T23:13:00.000+00:00</published><updated>2013-06-19T07:47:05.141+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Testing"/><title type='text'>Grails 0.5.5 - testing controllers with Mocks and Expando</title><content type='html'>In this blog entry I would like to show you how to unit test a typical &#39;update&#39; action in Grails using &lt;a href=&quot;http://groovy.codehaus.org/Groovy+Mocks&quot; target=&quot;_blank&quot; title=&quot;Groovy Mocks&quot;&gt;Groovy Mocks&lt;/a&gt; and the &lt;a href=&quot;http://groovy.codehaus.org/ExpandoMetaClass&quot; target=&quot;_blank&quot; title=&quot;Expando&quot;&gt;ExpandoMetaClass&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s assume we have an action like this:&lt;br /&gt;
&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt; 
 def updateword = {
     def word = Word.get( params.word_id )
     if(word) {
         word.pl = params.word_translation
         if(word.save()) {
             render(&quot;OK&quot;)
         }
         else {
             render(&quot;Error updating word&quot;)
         }
     }
     else {
         render(&quot;Word does not exist&quot;)
     }
 }&lt;/pre&gt;
In brief, it updates a Word instance by getting a Word from the database, modifying its &#39;pl&#39; property and saving it back to the database.&lt;br /&gt;
&lt;br /&gt;
Now, how would you unit test the execution path where the &quot;Error updating word&quot; message gets rendered? To do that, you need a scenario where a Word instance gets returned from the database but fails to save back after the update operation. Here is how you can do it using Mocks and Expandos:&lt;br /&gt;
&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;
 void testUpdateExistingWordSaveFailure() {
     def mock = new MockFor(Word)
     def fakeWord = new Expando(save: {false})
 
     mock.demand.get { fakeWord }
     mock.use {
         def wc = new WordController()
         wc.params.word_translation = &quot;tak&quot;
         wc.updateword()
         assertEquals &quot;Error updating word&quot;, 
                      wc.response.delegate.contentAsString
     }
 }&lt;/pre&gt;
&lt;br /&gt;
The flow of the test is as follows:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Create a mock for the Word class&lt;/li&gt;
&lt;li&gt;Craete a fake word instance (Expando) which replaces the &#39;save&#39; method&lt;/li&gt;
&lt;li&gt;Demand the Word class to return the faked word when the get method is called (exactly once)&lt;/li&gt;
&lt;li&gt;Use the mock and call the updateword closure&lt;/li&gt;
&lt;li&gt;Assert if the correct message was rendered to the browser&lt;/li&gt;
&lt;/ol&gt;
See the &lt;a href=&quot;http://grails.org/Testing+Controllers&quot; target=&quot;_blank&quot; title=&quot;Unit &amp;amp; Integration Testing Controllers&quot;&gt;Unit &amp;amp; Integration Testing Controllers&lt;/a&gt; page to understand the last step - getting a text from the response.</content><link rel='replies' type='application/atom+xml' href='http://marcinfo.blogspot.com/feeds/5382818263807243512/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/7818858158873072140/5382818263807243512' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/5382818263807243512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/5382818263807243512'/><link rel='alternate' type='text/html' href='http://marcinfo.blogspot.com/2007/06/grails-055-testing-controllers-with.html' title='Grails 0.5.5 - testing controllers with Mocks and Expando'/><author><name>Marcin Domanski</name><uri>http://www.blogger.com/profile/00857288385499196720</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://marcin.doman.googlepages.com/marcin.png'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7818858158873072140.post-3471890571747048188</id><published>2007-05-26T17:04:00.000+00:00</published><updated>2007-06-03T23:45:38.985+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Testing"/><title type='text'>Grails 0.5 Unit Testing - other issues</title><content type='html'>I have mentioned already about &lt;a target=&quot;_blank&quot; href=&quot;http://marcinfo.blogspot.com/2007/05/grails-unit-testing-not-agile.html&quot;&gt;slow unit testing&lt;/a&gt; in Grails 0.5. Apart from that, there are several other bugs:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a title=&quot;domain object are not deleted between test runs&quot; target=&quot;_blank&quot; href=&quot;http://archive.grails.codehaus.org/user/45ac42bc0705200017w131eb708gfb83e646a8b6933d%40mail.gmail.com&quot;&gt;domain objects are not deleted between test runs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a title=&quot;when deleting domain objects manually, current session must be flushed&quot; target=&quot;_blank&quot; href=&quot;http://archive.grails.codehaus.org/user/e3730d210705200127u10fb8e66od312449f371a399f%40mail.gmail.com&quot;&gt;when deleting domain objects manually, current session must be flushed&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a title=&quot;flushing does not work if a previous &#39;save&#39; method call did not work because of a validation error&quot; target=&quot;_blank&quot; href=&quot;http://archive.grails.codehaus.org/user/e3730d210705260142y26b9fd7do1d7647bfcda6bbe7%40mail.gmail.com&quot;&gt;flushing does not work when saving a domain class that didn&#39;t pass validation&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;Luckily, there are workarounds for all these bugs (good news if you are stuck). Check the grails-user group for more details (clicking one of the bugs above should take you to the description and solution).&lt;br /&gt;&lt;br /&gt;The good news is, that all the issues seem to be fixed in the current 0.5.5 snapshot (downloaded on 26th of May)! Suggest that you give it a go, or even upgrade your projects to version 0.5.5.&lt;br /&gt;&lt;br /&gt;Be careful however when upgrading to 0.5.5. Tests have been moved from &#39;grails-tests&#39; to &#39;test&#39; folder, therefore &#39;grails test-app&#39; will give you:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(102, 102, 102);&quot;&gt; No tests found in test/unit to execute ...&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: rgb(102, 102, 102);&quot;&gt; No tests found in test/integration to execute ...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;As you can see, tests have been separated into two categories: unit and integration. I have noticed however, that unit tests for domain classes are being generated under the &#39;integration&#39; test folder. Looks like a new bug, though more likely that the test separation has not been fully implemented (and tested :)) yet.&lt;br /&gt;&lt;span style=&quot;color: rgb(102, 102, 102);&quot;&gt;&lt;b&gt;&lt;br /&gt;Update:&lt;/b&gt; Actually, it is not a bug. Typical unit tests for domain classes in Grails are in fact integration tests with all the dynamic methods and properties injected by the framework. Plain unit tests can be created, too by running the &lt;tt&gt;grails create-unit-test &lt;/tt&gt; command. Read more on the &lt;a href=&quot;http://grails.org/Unit+Testing&quot;&gt;Grails Unit Testing&lt;/a&gt; page.&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://marcinfo.blogspot.com/feeds/3471890571747048188/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/7818858158873072140/3471890571747048188' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/3471890571747048188'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/3471890571747048188'/><link rel='alternate' type='text/html' href='http://marcinfo.blogspot.com/2007/05/grails-05-unit-testing-other-issues.html' title='Grails 0.5 Unit Testing - other issues'/><author><name>Marcin Domanski</name><uri>http://www.blogger.com/profile/00857288385499196720</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://marcin.doman.googlepages.com/marcin.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7818858158873072140.post-8300868334371752177</id><published>2007-05-21T20:54:00.000+00:00</published><updated>2007-06-03T23:45:11.146+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Grails"/><category scheme="http://www.blogger.com/atom/ns#" term="Testing"/><title type='text'>Grails Unit Testing not agile</title><content type='html'>Don&#39;t be surprised if you give Grails unit testing a try. I&#39;ve done that recently and must say it is not agile at all. At least not yet (Grails 0.5).&lt;br /&gt;&lt;br /&gt;As for now, it takes approximately 25 sec on a pretty fast box (my PC is a 3.2 GHz Dual Core with 2GB RAM, Os WinXp) to run a single unit test. Why so long? Surely, unit tests should run faster.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold; color: rgb(153, 153, 153);&quot;&gt;Update:&lt;/span&gt;&lt;span style=&quot;color: rgb(153, 153, 153);&quot;&gt; I&#39;ve just run grails test-app on my new Ubuntu 7.04. To my surprise, a single unit test run takes only 17 sec. That&#39;s a big difference comparing to 25 sec on WinXp.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I have found the answer on the grails user group. Graeme Rocher, the  project lead of Grails, in reply to a &lt;a href=&quot;http://archive.grails.codehaus.org/user/10685609.post%40talk.nabble.com&quot;&gt;similar issue&lt;/a&gt; says:&lt;br /&gt;&lt;blockquote&gt;&quot;We&#39;re separating out unit and integration tests so that unt tests will&lt;br /&gt;run quicker. At the moment every test is an integration test, hence&lt;br /&gt;why it takes long to bootstrap&quot; (&lt;a href=&quot;http://archive.grails.codehaus.org/user/3f2c19a70705200400l19d89aa6n627e3f58f3a4722e%40mail.gmail.com&quot;&gt;Graeme&#39;s post&lt;/a&gt;)&lt;/blockquote&gt;&lt;br /&gt;The slow unit testing should be improved in Grails 0.5.5, as Graeme is working on improvements to that. Can&#39;t wait to try this out as TDD on Grails can be really exciting then!&lt;br /&gt;&lt;br /&gt;Apart from that there is another small issue, though. Some have already experienced that (see &lt;a href=&quot;http://archive.grails.codehaus.org/user/45ac42bc0705200139h2ceab3fcub8f3d1d4a28ffeac%40mail.gmail.com&quot;&gt;this post&lt;/a&gt; on the grails-user group), too. If a unit test fails it does not tell you what went wrong. All you can see is the &#39;test failed&#39; message. Quite confusing, especially if you are used to JUnit&#39;s detailed error messages.&lt;br /&gt;&lt;br /&gt;Luckily, you can find the cause. Again, the grails-user group is where I&#39;ve found &lt;a href=&quot;http://archive.grails.codehaus.org/user/e3730d210705200148m30125380k64fda7a99b56514c%40mail.gmail.com&quot;&gt;the answer&lt;/a&gt;. The detailed test reports sit under target/test-reports folder. They are available in different formats, including plain text, xml and html. You can find the detailed message there, plus a very long stack trace ;-) The only trouble is that you waste even more time finding the cause of a unit test failure...&lt;br /&gt;&lt;br /&gt;And here I can give one hint that makes it faster. If you open one of the detailed reports in a text editor that checks for file modifications, it would reload the report automatically after each test run ;-)</content><link rel='replies' type='application/atom+xml' href='http://marcinfo.blogspot.com/feeds/8300868334371752177/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/7818858158873072140/8300868334371752177' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/8300868334371752177'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/8300868334371752177'/><link rel='alternate' type='text/html' href='http://marcinfo.blogspot.com/2007/05/grails-unit-testing-not-agile.html' title='Grails Unit Testing not agile'/><author><name>Marcin Domanski</name><uri>http://www.blogger.com/profile/00857288385499196720</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://marcin.doman.googlepages.com/marcin.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7818858158873072140.post-3488515927482915172</id><published>2007-05-19T19:48:00.000+00:00</published><updated>2007-05-19T20:00:16.274+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Rails"/><title type='text'>Joke on Rails</title><content type='html'>Just inside the gates of heaven, St. Peter sits at a desk checking people in.&lt;br /&gt;&lt;br /&gt;Peter: .Welcome to heaven. Programming language?.&lt;br /&gt;&lt;br /&gt;The man at the front of the line says, .Smalltalk..&lt;br /&gt;&lt;br /&gt;Glancing at his clipboard, Peter says, .Room 33. Be very quiet as you pass Room Six..&lt;br /&gt;&lt;br /&gt;The process repeats itself with the next person in line:&lt;br /&gt;&lt;br /&gt;Peter: .Welcome to heaven. Programming language?.&lt;br /&gt;&lt;br /&gt;Person #2: .Common Lisp..&lt;br /&gt;&lt;br /&gt;Peter: .Room 17. Be very quiet as you pass Room Six..&lt;br /&gt;&lt;br /&gt;The next person moves to the front of the line with a look of curiosity on her face.&lt;br /&gt;&lt;br /&gt;Peter: .Welcome to heaven. Programming language?.&lt;br /&gt;&lt;br /&gt;Person #3: .Python..&lt;br /&gt;&lt;br /&gt;Peter: .Room 54. Be very quiet as you pass Room Six..&lt;br /&gt;&lt;br /&gt;Person #3: .Why do you keep telling us to be quiet as we pass Room Six?.&lt;br /&gt;&lt;br /&gt;Peter: .Because the Ruby on Rails People are in Room Six, and they think they&#39;re the only ones here.&lt;br /&gt;&lt;br /&gt;Found &lt;a target=&quot;_blank&quot; href=&quot;http://forum.rubyonrails.pl/viewtopic.php?id=414&quot;&gt;here&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://marcinfo.blogspot.com/feeds/3488515927482915172/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/7818858158873072140/3488515927482915172' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/3488515927482915172'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/3488515927482915172'/><link rel='alternate' type='text/html' href='http://marcinfo.blogspot.com/2007/05/joke-on-rails.html' title='Joke on Rails'/><author><name>Marcin Domanski</name><uri>http://www.blogger.com/profile/00857288385499196720</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://marcin.doman.googlepages.com/marcin.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7818858158873072140.post-7750185210863523175</id><published>2007-05-10T23:16:00.000+00:00</published><updated>2008-01-14T23:23:48.105+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Rails"/><category scheme="http://www.blogger.com/atom/ns#" term="TDD"/><title type='text'>TDD on Rails on Mac</title><content type='html'>Together with &lt;a title=&quot;Andrew&quot; target=&quot;_blank&quot; href=&quot;http://andrzejkrzywda.com/&quot;&gt;Andrew&lt;/a&gt;, we had an instructive TDD session on Rails yesterday. It was quite a new experience especially for me as I had never had a Mac in my hands before :). It took me about 2 hours to get used to it. However, there is still one thing I don&#39;t like and can&#39;t understand - where is the &#39;Del&#39; button on Mac??!! Hope there is a good explanation for not having it ...&lt;br /&gt;&lt;br /&gt;Anyway, we started from scratch repeating the &lt;a title=&quot;15 TDD steps&quot; target=&quot;_blank&quot; href=&quot;http://andrzejonsoftware.blogspot.com/2007/05/15-tdd-steps-to-create-rails.html&quot;&gt;15 TDD steps&lt;/a&gt; to get the basic app running. The app itself is very simple - a kind of english/polish dictionary with the guess/learn option. The next thing after the 15th step was to add some content to the database so the user can play around and have fun guessing new words. Andrew talks in more detail in &lt;a title=&quot;his last post&quot; href=&quot;http://andrzejkrzywda.com/2007/05/and-some-more-tdd-steps-with-rails.html&quot;&gt;his last post&lt;/a&gt; how we tested and implemented that. I must admit however that it was a real fun for a (still!) Java programmer like me to switch to Ruby for a while. Just see how little code we had to write to achieve the following:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;check if a Word has a translation&lt;/li&gt;&lt;/ul&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;def translated?&lt;br /&gt;  pl and en&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;return a random and translated word&lt;/li&gt;&lt;/ul&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;def self.random&lt;br /&gt;  (Word.find(:all).select {|word| word.translated?}.sort_by {rand})[0]&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Can you imagine how would this code be implemented in Java?&lt;br /&gt;&lt;br /&gt;Having done some TDD before I realized again what really matters when you test drive your application. TDD changes the way of thinking of the code you are going to write. As Andrew said, it is all about design, not necessarily about testing itself. The tests you write is some sort of (positive) side effect - you just get them as you go along.&lt;br /&gt;&lt;br /&gt;The other thing I&#39;d like to mention here is YAGNI (&lt;a target=&quot;_blank&quot; href=&quot;http://c2.com/xp/YouArentGonnaNeedIt.html&quot;&gt;You Arent Gonna Need It&lt;/a&gt;). Perhaps a bit off topic, nevertheless that&#39;s what came to my mind yesterday, too.&lt;br /&gt;Having written many lines of good looking and performing code I had to stop worrying about code like this:&lt;br /&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;(Word.find(:all).select {|word| word.translated?}.sort_by {rand})[0]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Certainly getting all the words from the database does not look nice at first glance. But have a think - is that really going to work very slow and cause any problems? Not really, unless you have hundreds or thousands of users, which doesn&#39;t happen so quickly. So don&#39;t bother until this becomes a real problem. And what is the most important here - be productive and don&#39;t waste your precious time for improvements that don&#39;t have any business value.</content><link rel='replies' type='application/atom+xml' href='http://marcinfo.blogspot.com/feeds/7750185210863523175/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/7818858158873072140/7750185210863523175' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/7750185210863523175'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/7750185210863523175'/><link rel='alternate' type='text/html' href='http://marcinfo.blogspot.com/2007/05/tdd-on-rails-on-mac.html' title='TDD on Rails on Mac'/><author><name>Marcin Domanski</name><uri>http://www.blogger.com/profile/00857288385499196720</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://marcin.doman.googlepages.com/marcin.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7818858158873072140.post-104316842657800451</id><published>2007-01-16T23:30:00.000+00:00</published><updated>2008-01-14T23:26:17.243+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="SwingBuilder"/><category scheme="http://www.blogger.com/atom/ns#" term="System Tray"/><title type='text'>Groovy in System Tray</title><content type='html'>As of Java 1.6 there is a cool feature available - System Tray. Actually, System Trasy plus nice notifications which look like a balloon on Windows. Now you can put Duke next to your favorite icons, if you like!&lt;br /&gt;&lt;br /&gt;An example source code is available on the &lt;a title=&quot;System Tray example&quot; href=&quot;http://java.sun.com/javase/6/docs/api/java/awt/SystemTray.html&quot;&gt;java docs page&lt;/a&gt;. Indeed, it is an example and even not a fully working one. I am not going to rewrite it - I believe Sun it works. Instead, I will try making the code more ... groovy ;-)&lt;br /&gt;&lt;br /&gt;Here it is, System Tray example in Groovy:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;import java.awt.Image&lt;br /&gt;import java.awt.AWTException&lt;br /&gt;import java.awt.MenuItem&lt;br /&gt;import java.awt.PopupMenu&lt;br /&gt;import java.awt.SystemTray&lt;br /&gt;import java.awt.Toolkit&lt;br /&gt;import java.awt.TrayIcon&lt;br /&gt;&lt;br /&gt;def trayIcon = null&lt;br /&gt;&lt;br /&gt;def exit = { e -&gt;&lt;br /&gt;  println &quot;Exiting ...&quot;&lt;br /&gt;  System.exit(0)&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;def showMessage = { e -&gt;&lt;br /&gt;  trayIcon.displayMessage(&quot;Action Event&quot;,&lt;br /&gt;                        &quot;An Action Event Has Been Peformed!&quot;,&lt;br /&gt;                        TrayIcon.MessageType.INFO)&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;if (SystemTray.isSupported()) {&lt;br /&gt;  def tray = SystemTray.getSystemTray()&lt;br /&gt;&lt;br /&gt;  def image = Toolkit.getDefaultToolkit().&lt;br /&gt;                        getImage(&quot;duke16.gif&quot;)&lt;br /&gt;&lt;br /&gt;  def popup = new PopupMenu()&lt;br /&gt;  popup.add(new MenuItem(label:&quot;Exit&quot;, actionPerformed:exit))&lt;br /&gt;&lt;br /&gt;  trayIcon = new TrayIcon(image:image, tooltip:&quot;Tray Demo&quot;,&lt;br /&gt;                          popup:popup, imageAutoSize:true,&lt;br /&gt;                          actionPerformed:showMessage)&lt;br /&gt;  try {&lt;br /&gt;    tray.add(trayIcon)&lt;br /&gt;  } catch (AWTException e) {&lt;br /&gt;    println e&lt;br /&gt;  }&lt;br /&gt;} else {&lt;br /&gt;  prinltn &quot;System Tray not supported&quot;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see it is possible to make Duke shout: Groovy did it! However, the code looks more like java. And what is worse, I am afraid we can&#39;t make it more groovy, using Swing Builder (the popup menu could be &#39;built&#39;). The Tray functionality sits under java.awt package, which the Swing Builder does not support I think (well, it did not work for me). Luckily, we can still use Groovy&#39;s closures instead of Action Listeners.&lt;br /&gt;&lt;br /&gt;Nevertheless, it works fine and - what is worth stressing here - it is platform independent! I have run the example successfully on GNOME, KDE and Win Xp desktops. Here are my screenshots:&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;table border=&quot;0&quot; cellpadding=&quot;3&quot; cellspacing=&quot;0&quot; width=&quot;100%&quot;&gt;&lt;tbody&gt;&lt;tr align=&quot;center&quot;&gt;&lt;td width=&quot;100%&quot;&gt;&lt;img style=&quot;height: 74px; width: 271px;&quot; title=&quot;System Tray on GNOME desktop&quot; src=&quot;http://docs.google.com/File?id=dd72qjb_18qsxzrx&quot; border=&quot;1&quot; hspace=&quot;0&quot; vspace=&quot;0&quot; /&gt;&lt;br /&gt;GNOME&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr align=&quot;center&quot;&gt;&lt;td width=&quot;100%&quot;&gt;&lt;img style=&quot;height: 93px; width: 270px;&quot; title=&quot;System Tray on KDE Desktop&quot; src=&quot;http://docs.google.com/File?id=dd72qjb_157bbh9d&quot; border=&quot;1&quot; hspace=&quot;0&quot; vspace=&quot;0&quot; /&gt;&lt;br /&gt;KDE&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr align=&quot;center&quot;&gt;&lt;td width=&quot;100%&quot;&gt;&lt;img style=&quot;height: 84px; width: 216px;&quot; title=&quot;System Tray on Windows Xp&quot; src=&quot;http://docs.google.com/File?id=dd72qjb_13dvks5m&quot; border=&quot;1&quot; hspace=&quot;0&quot; vspace=&quot;0&quot; /&gt;&lt;br /&gt;Windows Xp&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr align=&quot;center&quot;&gt;&lt;td width=&quot;100%&quot;&gt;&lt;img style=&quot;height: 16px; width: 16px;&quot; title=&quot;Duke 16x16&quot; src=&quot;http://docs.google.com/File?id=dd72qjb_16g3t4vm&quot; /&gt; &lt;img style=&quot;height: 32px; width: 32px;&quot; title=&quot;Duke 32x32&quot; src=&quot;http://docs.google.com/File?id=dd72qjb_17d3z9h3&quot; /&gt;&lt;br /&gt;And two Dukes, if you would like to run the example on your desktop&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Oh, and one more thing. If you are curious what other dynamic languages support System Tray, my friend, Andrzej, shows how to do this in &lt;a href=&quot;http://www.codeplex.com/Wiki/View.aspx?ProjectName=IronPython&quot;&gt;Iron Python&lt;/a&gt;. See his article &lt;a title=&quot;System Tray example in Iron Python&quot; href=&quot;http://andrzejonsoftware.blogspot.com/2007/01/iron-python-on-balloons.html&quot;&gt;here&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://marcinfo.blogspot.com/feeds/104316842657800451/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/7818858158873072140/104316842657800451' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/104316842657800451'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/104316842657800451'/><link rel='alternate' type='text/html' href='http://marcinfo.blogspot.com/2007/01/groovy-in-system-tray-as-of-java-1.html' title='Groovy in System Tray'/><author><name>Marcin Domanski</name><uri>http://www.blogger.com/profile/00857288385499196720</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://marcin.doman.googlepages.com/marcin.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7818858158873072140.post-1480246419802526022</id><published>2007-01-11T22:46:00.000+00:00</published><updated>2008-12-08T23:03:25.648+00:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AntBuilder"/><category scheme="http://www.blogger.com/atom/ns#" term="Groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="SVN"/><category scheme="http://www.blogger.com/atom/ns#" term="SwingBuilder"/><title type='text'>Groovy at work - developing a GUI build tool</title><content type='html'>Suppose you have a complicated Ant build which builds your latest code from SVN. It is very complicated and running it requires a set of parameters to be passed into it. This involves creating and maintaining a set of properties files and probably also batch files which start the build as you don&#39;t want to manually type in all props every time. Also, source files need to be updated before running the build. Does it not sound like a tough manual and repeatable work?&lt;br /&gt;&lt;br /&gt;How about a simple GUI where you could type in (or even choose from drop downs, if possible) required properties and run the build by clicking one button? Yes, why not, but we don&#39;t want to waste two weeks or more writing such a tool, in Swing for example. Ok, how about Groovy and its brilliant features, like SwingBuilder, AntBuilder and closures? Hmm, that sounds much better!&lt;br /&gt;&lt;br /&gt;Recently I have developed such a GUI build tool using Groovy. It took me 2 days to code it. Now guess what features it has:&lt;br /&gt;&lt;ul&gt;   &lt;li&gt;     full checkout from SVN to a local directory   &lt;/li&gt;   &lt;li&gt;     project update before each build&lt;br /&gt;&lt;/li&gt;   &lt;li&gt;     running the old Ant build, written in XML   &lt;/li&gt;   &lt;li&gt;     intercepting build output for error messages&lt;/li&gt;&lt;li&gt;reading tool properties from an external XML config&lt;br /&gt;&lt;/li&gt;    &lt;/ul&gt;  All in about 350 lines of groovy code! Most of the code are &lt;span style=&quot;font-style: italic;&quot;&gt;SwingBuilder&lt;/span&gt; closures where I am building the GUI and &lt;span style=&quot;font-style: italic;&quot;&gt;AntBuilder&lt;/span&gt; doing the build job. The most interesting parts however are:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Defining external svn ant task&lt;/span&gt;&lt;br /&gt;To get that to work some additional jars are needed. Assuming they are under     local lib directory, the svn ant task can be defined like that:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;def PATH = &quot;task.path&quot;&lt;br /&gt;ant.path(id:PATH) {&lt;br /&gt;  ant.pathelement(location:&quot;lib/ganymed.jar&quot;)&lt;br /&gt;  ant.pathelement(location:&quot;lib/svnkit.jar&quot;)&lt;br /&gt;  ant.pathelement(location:&quot;lib/svnant.jar&quot;)&lt;br /&gt;  ant.pathelement(location:&quot;lib/svnClientAdapter.jar&quot;)&lt;br /&gt;  ant.pathelement(location:&quot;lib/svnkit-javahl.jar&quot;)&lt;br /&gt;}&lt;br /&gt;ant.taskdef(&lt;br /&gt;  resource:&quot;org/tigris/subversion/svnant/svnantlib.xml&quot;,&lt;br /&gt;  classpathref:PATH)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Accessing SVN&lt;/span&gt;&lt;br /&gt;Now, having the svn ant task defined, accessing svn (checkout, update etc.) is as easy as:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;def svnCall = { task -&gt;&lt;br /&gt;  ant.svn(username:USER, password:PASS) {&lt;br /&gt;   task()&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;where the task can be svn checkout:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;svnCall {&lt;br /&gt;  ant.checkout(url:url, destpath:dest)&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;or svn update:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;svnCall {&lt;br /&gt;  ant.update(dir:dest)&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see there is no code duplication. The ant.svn call is common for     all svn tasks. I am passing a closure as a parameter into it, which then     can call ant.checkout or ant.update. Closures - it&#39;s brilliant, isn&#39;t it?&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Running an external Ant build and     reading output info.&lt;/span&gt;&lt;br /&gt;The XML ant build needs to be started from command line (no, we are not     going to rewrite it using Gant, not yet ;-) ). How do we do this in Grooovy?&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;def build = &quot;cmd /c ant -f build.xml&lt;br /&gt;        -propertyfile build.properties target&quot;.execute()&lt;br /&gt;&lt;/pre&gt;The build variable is actually a Process instance, so we can now intercept     the in and err streams:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;build.in.eachLine { line -&gt;&lt;br /&gt;  //consume each line&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;build.err.eachLine { line -&gt;&lt;br /&gt;  //consume each line&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Why intercepting the err stream? Well, if the build fails Ant writes BUILD     FAILED + the error to that stream. Then I can show an info message using JOptionPane as MsgBOX:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;import javax.swing.JOptionPane as MsgBOX&lt;br /&gt;...&lt;br /&gt;MsgBOX.showMessageDialog(gui, message, title,&lt;br /&gt;                     MsgBOX.INFORMATION_MESSAGE)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;The build tool&lt;/span&gt;&lt;br /&gt;To give you an idea what the build tool looks like, here is a screenshot (I have removed real labels/data) :&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBBMHFXXTb45lrZbRipBbLAWvDDTpkh1whsC3xS7IrQ7iEyFPR9fBbqr2joeGKhv2SE8g5cEBEEHN5zwaSTkgzobkFBYlRYnVtcx47_yv62Ak-NyJhewVoSPCDZYmyhcDL_OirlNvWJV8/s1600-h/buildTool.png&quot;&gt;&lt;img style=&quot;margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBBMHFXXTb45lrZbRipBbLAWvDDTpkh1whsC3xS7IrQ7iEyFPR9fBbqr2joeGKhv2SE8g5cEBEEHN5zwaSTkgzobkFBYlRYnVtcx47_yv62Ak-NyJhewVoSPCDZYmyhcDL_OirlNvWJV8/s320/buildTool.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5019072287513912482&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;Title borders, drop downs, text fields, labels and box layout - all in 2 days? Yep, plus event handling for all the combo boxes, which make up some sort of hierarchy, actually. This makes the tool a bit complicated, but not the code! Event handling is as easy as clicking a button (actionPerformed as a closure):&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;def comboBox = swing.comboBox(items:items,&lt;br /&gt;                          actionPerformed:action)&lt;br /&gt;&lt;/pre&gt;and the hierarchy is read from an external XML file, using Groovy&#39;s XML parser:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;def XML = new File(xmlFile).getText()&lt;br /&gt;root = new XmlParser().parseText(XML)&lt;br /&gt;&lt;/pre&gt;Nice and simple, isn&#39;t it?&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Summary&lt;/span&gt;&lt;br /&gt;It was such a pleasure writing the tool, in no time actually. I have to admit that it was my first bigger piece of Groovy code, which now really works fine in my company! What else can I say - thank you, Groovy! You saved me at least a week of swing coding in Java!&lt;br /&gt;&lt;a style=&quot;display:none&quot; onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1ubu306tGzFn7StuaqoLKue1ZEd0A1k1pFIjNGhcbw-5lZ2Kg5UIlaO32cc7OzLUehSjeKzXMoVrc_mR7HgXnlSoiCFQVH_vd05J8nxEPxH6DosMvaqFNbs4ave-hs3w-w1Jew7opDfE/s1600-h/marcindomanski.jpg&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1ubu306tGzFn7StuaqoLKue1ZEd0A1k1pFIjNGhcbw-5lZ2Kg5UIlaO32cc7OzLUehSjeKzXMoVrc_mR7HgXnlSoiCFQVH_vd05J8nxEPxH6DosMvaqFNbs4ave-hs3w-w1Jew7opDfE/s320/marcindomanski.jpg&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5073074222250521170&quot; /&gt;&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://marcinfo.blogspot.com/feeds/1480246419802526022/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/7818858158873072140/1480246419802526022' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/1480246419802526022'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7818858158873072140/posts/default/1480246419802526022'/><link rel='alternate' type='text/html' href='http://marcinfo.blogspot.com/2007/01/groovy-at-work-developing-gui-build.html' title='Groovy at work - developing a GUI build tool'/><author><name>Marcin Domanski</name><uri>http://www.blogger.com/profile/00857288385499196720</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='27' height='32' src='http://marcin.doman.googlepages.com/marcin.png'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBBMHFXXTb45lrZbRipBbLAWvDDTpkh1whsC3xS7IrQ7iEyFPR9fBbqr2joeGKhv2SE8g5cEBEEHN5zwaSTkgzobkFBYlRYnVtcx47_yv62Ak-NyJhewVoSPCDZYmyhcDL_OirlNvWJV8/s72-c/buildTool.png" height="72" width="72"/><thr:total>4</thr:total></entry></feed>