<?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-19648744</id><updated>2024-09-13T09:35:25.651-04:00</updated><category term="netbeans"/><category term="grails"/><category term="groovy"/><category term="tapestry"/><category term="j2me"/><category term="netbeans rcp"/><category term="apple"/><category term="jsf"/><category term="rails"/><category term="scala"/><category term="grapestry"/><category term="jFreeSafe"/><category term="junit"/><category term="my thesis"/><category term="scripting"/><category term="java"/><category term="jython"/><category term="kojo"/><category term="legacy rails"/><category term="mobile video"/><category term="rants"/><category term="testing"/><category term="antivirus"/><category term="blogger"/><category term="eclipse"/><category term="iphone"/><category term="jboss"/><category term="jdo"/><category term="jemmy"/><category term="linux"/><category term="maven"/><category term="mobility"/><category term="oracle"/><category term="plugins"/><category term="redhat"/><category term="scalatest"/><category term="selenium"/><category term="symantec"/><category term="tynamo"/><category term="ui-elements"/><category term="web flow"/><category term="wicket"/><title type='text'>Alex Kotchnev&#39;s Blog</title><subtitle type='html'>Exploring Scala, NetBeans,  Tapestry, PaaS, and other cool stuff.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default?alt=atom&amp;redirect=false'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default?alt=atom&amp;start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>59</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-19648744.post-1998399601504744976</id><published>2014-12-21T23:29:00.000-05:00</published><updated>2015-01-06T16:33:46.762-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="maven"/><category scheme="http://www.blogger.com/atom/ns#" term="netbeans"/><category scheme="http://www.blogger.com/atom/ns#" term="scala"/><category scheme="http://www.blogger.com/atom/ns#" term="scalatest"/><title type='text'>Debugging ScalaTest tests in NetBeans with the Maven ScalaTest Plugin</title><content type='html'>In the spirit of creating blog posts whenever there is something important that I&#39;ve figured out and I should not lose or forget, here I go.&lt;br /&gt;
&lt;br /&gt;
A month or two ago, we moved all of our Java tests at&amp;nbsp;&lt;a href=&quot;http://www.apprenda.com/&quot;&gt;Apprenda&lt;/a&gt;&amp;nbsp;to run using the &lt;a href=&quot;http://www.scalatest.org/user_guide/using_the_scalatest_maven_plugin&quot;&gt;Maven Scalatest plugin&lt;/a&gt;&amp;nbsp;. We have been using ScalaTest from day one; however, for a while there we were using SureFire&amp;nbsp;+ @RunWith +&amp;nbsp;JUnitRunner annotations to kick off the tests. However, since IntelliJ was quite happy to run ScalaTest tests without anything else (e.g. like the @RunWith and JUnitRunner annotations), we would sometimes end up in a situation where we have some tests that are created during development in the IDE; however, when the build is run using just plain Maven, it would skip over these tests.&lt;br /&gt;
&lt;br /&gt;
So, the as described in the &lt;a href=&quot;http://www.scalatest.org/user_guide/using_the_scalatest_maven_plugin&quot;&gt;ScalaTest documentation on working w/ the plugin&lt;/a&gt;&amp;nbsp;, you first disable the surefire plugin, and then add the ScalaTest Maven plugin to run the tests. That all worked great, and now all of our tests run, regardless of whether we remembered to add the&amp;nbsp;@RunWith annotation.&lt;br /&gt;
&lt;br /&gt;
That presented a problem for me : when we were running the tests with SureFire, NetBeans was more than happy to run and debug the tests. However, when we switched to the Maven ScalaTest plugin - no such luck .. what do to ? I&#39;m totally fine with not seeing the graphical test runner results that NetBeans shows w/ JUnit Tests ; however, where it becomes a problem is when I can&#39;t run or debug the individual tests from the IDE. The tests run just fine when you &quot;Test&quot; the whole project (e.g. right click on the project and choose &quot;Test&quot; from the context menu), but if you try to run an individual test (e.g. right click on a test case and choose Test from the context menu), or when you&#39;re inside the test case go to the Run -&amp;gt; Test File menu you&#39;re out of luck, nothing shows up.&lt;br /&gt;
&lt;br /&gt;
So, getting the single test to run from NetBeans turns out to be pretty simple :&lt;br /&gt;
1. In the Project Properties -&amp;gt; Actions panel, choose the &quot;Test File&quot; action, change the properties from &quot;&lt;b&gt;test=${packageClassName}&lt;/b&gt;&quot; to &quot;&lt;b&gt;suites=${packageClassName}&lt;/b&gt;&quot; . As indicated in the ScalaTest documentation, the way to tell ScalaTest what test to run is using the &lt;b&gt;suites&amp;nbsp;&lt;/b&gt;system property, e.g. :&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhGl-VnUL1Xh53euOueLCC_obEBtc_gwZpDJX0dGqRsRG2PWPuSWikapFF9CTUTjQ-eDMEdwMDNKB5Op9GbZAWmkuO3tMn6lNVJEUFVhx90uAP7GaMH304sG3Jx7FdLZtk1gjlOw/s1600/Project+Properties+-+lost-n-found_034.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhGl-VnUL1Xh53euOueLCC_obEBtc_gwZpDJX0dGqRsRG2PWPuSWikapFF9CTUTjQ-eDMEdwMDNKB5Op9GbZAWmkuO3tMn6lNVJEUFVhx90uAP7GaMH304sG3Jx7FdLZtk1gjlOw/s1600/Project+Properties+-+lost-n-found_034.png&quot; height=&quot;336&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, that&#39;s great, but how do you debug the test ? Looking at the ScalaTest documentation I was a bit confused on how to do this, as there were a few options related to debugging, but which ones to set in order to get this from NetBeans, it wasn&#39;t entirely clear.&lt;br /&gt;
&lt;br /&gt;
It turns out the fix was fairly simple :&lt;br /&gt;
1. Since by default NetBeans runs surefire:test to run individual tests, the goal that is run needed to be switched to just &quot;test&quot; - this allows the Maven Scalatest plugin to run (as it binds to the test phase and does nothing for surefire:test)&lt;br /&gt;
2. I had to add &amp;nbsp;a couple of additional system properties that ScalaTest needs in order to run the right test, turn on debugging, and connect the NetBeans debugger and the ScalaTest test that is running.&lt;br /&gt;
&lt;br /&gt;
#This teslls scalatest to run the test with this class&lt;br /&gt;
&lt;b&gt;suites=${packageClassName}&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
#These tell ScalaTest to start the new process with debugging enabled&lt;br /&gt;
&lt;b&gt;logForkedProcessCommand=true&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;debugForkedProcess=true&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;debugArgLine=-Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=${jpda.address}&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
# This tells netbeans to fire up the debugger and wait for a connection&lt;br /&gt;
&lt;b&gt;jpda.listen=true&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
Here&#39;s an example of what it looks like :&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirQmrrT60nx9lmaik8Qd0NeaiSd36dTZw2vwvXKa27SNKS5kNqUUxWKM6iD6SKiV03JIvsLOTdiNPn2ga_xFuiVImzeH91areYM_i4TBcMx3K6yDVK1QtCeU9Go5F6_TaP0beHWw/s1600/Project+Properties+-+lost-n-found_036.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirQmrrT60nx9lmaik8Qd0NeaiSd36dTZw2vwvXKa27SNKS5kNqUUxWKM6iD6SKiV03JIvsLOTdiNPn2ga_xFuiVImzeH91areYM_i4TBcMx3K6yDVK1QtCeU9Go5F6_TaP0beHWw/s1600/Project+Properties+-+lost-n-found_036.png&quot; height=&quot;464&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Tada - enjoy being able to run and debug individual ScalaTest tests right from NetBeans !&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/1998399601504744976/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2014/12/debugging-scalatest-tests-in-netbeans.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/1998399601504744976'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/1998399601504744976'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2014/12/debugging-scalatest-tests-in-netbeans.html' title='Debugging ScalaTest tests in NetBeans with the Maven ScalaTest Plugin'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhGl-VnUL1Xh53euOueLCC_obEBtc_gwZpDJX0dGqRsRG2PWPuSWikapFF9CTUTjQ-eDMEdwMDNKB5Op9GbZAWmkuO3tMn6lNVJEUFVhx90uAP7GaMH304sG3Jx7FdLZtk1gjlOw/s72-c/Project+Properties+-+lost-n-found_034.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-3902748969664883837</id><published>2014-03-07T01:07:00.000-05:00</published><updated>2014-03-07T23:04:26.602-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="kojo"/><category scheme="http://www.blogger.com/atom/ns#" term="scala"/><title type='text'>Intro to Programming : Drawing a House</title><content type='html'>&lt;h3&gt;
What is this&amp;nbsp;&lt;/h3&gt;
To help everyone along in getting started with drawing the Minecraft Creeper, I asked my son to put together a program that draws a house. It uses a lot of similar elements to the Creeper that the students need to draw, and might show a few new things that we didn&#39;t cover in class yet.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
The Code&lt;/h3&gt;
&lt;div&gt;
&lt;pre class=&quot;prettyprint lang-scala linenums&quot; name=&quot;code&quot;&gt;   
  clear()
  println(&quot;setAnimationDelay() sets how fast the turtle speed is, invisible makes the turtle invisible but the line is still there&quot;)
  println(&quot;penUp() make the line disapear but the turtle doesn&#39;t, setFillColor(Color(0, 0 ,0)) and setPencolor(Color(0, 0, 0,))  changes the color&quot;)
  println(&quot;repeat(){ } makes the program repeat as many time as you want&quot;)
  setAnimationDelay(100)
  invisible()
  penUp()
  left()
  forward(150)
  setFillColor(Color(255, 0, 51))
  setPenColor(Color(255, 0, 51))
  penDown()
  right()
  //makes the square for the house, note the usage of &#39;repeat&#39;
  repeat(4){
    forward(150)
    right()
  }
  forward(150)
  //this makes the roof
  setFillColor(Color(51, 51, 0))
  setPenColor(Color(51, 51, 0))
  right(60)
  forward(90)
  right(60)
  forward(90)
  penUp()
  //this makes the windows
  setFillColor(Color(51, 153, 255))
  setPenColor(Color(51, 153, 255))
  right(60)
  forward(40)
  right()
  forward(20)
  penDown()
  forward(30)
  right()
  forward(30)
  right()
  forward(30)
  right()
  right()
  penUp()
  forward(80)
  penDown()
  forward(30)
  left()
  forward(30)
  left()
  forward(30)
  left()
  forward(30)
  left()
  penUp()
  left()
  forward(80)
  penDown()
  //this makes the door
  setPenColor(Color(153, 68, 0))
  setFillColor(Color(153, 68, 0))
  forward(60)
  left()
  forward(35)
  left()
  forward(60)
  left()
&lt;/pre&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h3&gt;
The Outcome&lt;/h3&gt;
&lt;div&gt;
The finished product looks something like this&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPTkHMBP1gVyavM0xXjqE70lAjjItRERwkI2HvE1YdTSxKjNa75wI3QQ2UXMfqYxXNwZ1iBpPmt-TSzErLQaccsTqFi6c0_mIppUVOKI3qC9JtdGBLsXbg2RmXeoE8jIkuAM8X0g/s1600/Selection_022.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPTkHMBP1gVyavM0xXjqE70lAjjItRERwkI2HvE1YdTSxKjNa75wI3QQ2UXMfqYxXNwZ1iBpPmt-TSzErLQaccsTqFi6c0_mIppUVOKI3qC9JtdGBLsXbg2RmXeoE8jIkuAM8X0g/s1600/Selection_022.png&quot; height=&quot;251&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/3902748969664883837/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2014/03/intro-to-programming-drawing-house.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/3902748969664883837'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/3902748969664883837'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2014/03/intro-to-programming-drawing-house.html' title='Intro to Programming : Drawing a House'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPTkHMBP1gVyavM0xXjqE70lAjjItRERwkI2HvE1YdTSxKjNa75wI3QQ2UXMfqYxXNwZ1iBpPmt-TSzErLQaccsTqFi6c0_mIppUVOKI3qC9JtdGBLsXbg2RmXeoE8jIkuAM8X0g/s72-c/Selection_022.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-6737429199803600183</id><published>2014-03-07T00:49:00.001-05:00</published><updated>2014-03-07T23:08:12.479-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="kojo"/><category scheme="http://www.blogger.com/atom/ns#" term="scala"/><title type='text'>Intro to Programming : The First Class</title><content type='html'>&lt;h3 style=&quot;text-align: center;&quot;&gt;
.. And so it begins...&amp;nbsp;&lt;/h3&gt;
&lt;br /&gt;
This spring, I am teaching an &quot;Intro to Programming&quot; class at the After School Enrichment program at Craig Elementary, so this blog will get some new content with the progress of the class.&lt;br /&gt;
&lt;br /&gt;
&lt;h3 style=&quot;text-align: center;&quot;&gt;
The First Class&lt;/h3&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
In the first class, we covered quite a lot of ground : we learned quite a bit about each other, what the students in the class were interested in. As expected, most kids do enjoy video games, like learning how things work, and just plain enjoy working with computers. We briefly chatted about why people program in the first place : to express themselves, to tackle new challenges, and to be able to harness the power of computers for their own inventions.&lt;br /&gt;
&lt;br /&gt;
We covered some of the basic concepts of what a program is : a sequence of instructions that follow some specific grammar rules (just like they do in English). We had a quick introduction to &lt;a href=&quot;http://www.kogics.net/kojo&quot;&gt;Kojo&lt;/a&gt; (our programming environment, more on that later) and the various elements of Kojo&#39;s user interface - the command / script area, the drawing area, the menus, the output / error message area. Most importantly, we focused on how to continue learning from the environment - using code completion / prompts to see the syntax of commands that we hadn&#39;t used before, reading the command documentation. We also spent some time learning to recover from failure, when something unexpected happens - resetting the zoom level on the drawing area (if one accidentally zoomed out too far), and just bringing up the right windows if one of them suddenly disappeared.&lt;br /&gt;
&lt;br /&gt;
Our first programming exercise really didn&#39;t involve a computer : I asked my students to &quot;program me&quot; and command me from one side of the room to the other, while avoiding the obstacles along the way - chairs, large metal poles, backpacks. I did attempt to behave like a computer - I just followed the instructions that were given to me and failed a few times along the way - ran into an office chair, gonked my head against the metal pole. In order to tie up with what came next (which was drawing a square in our programming environment), I then asked the kids to use the same commands that they used to guide me through the room, but this time, to make me walk in a square along the tiled floor using the same limited set of commands - e.g. left, right, forward, back.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, with the basic commands under our belt, the kids got to programming. We learned how to draw a square. First everyone came up with a short program for a square, and then we experimented with variations on the topic - some of the kids really enjoyed drawing BIG squares and seeing the turtle in action while it was drawing the squares. Then, for the more ambitious students, we tried drawing a triangle (and yes, this is where the geometry skills came shining through - what were the angles in an equilateral triangle, you ask :-) ). The example looks something like this :&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiB8__0V0NXefByIgUkONMRP4ZOaYKsFNQsNyGNBZncGRnad_qTekj76cXYGc59oY5AtZ3USJcHKwvNJWb_S-9vLJVIjp6XxmcDIq1N93W3aysibRt0smcHyho_3ri6vKjhlQVhGw/s1600/Selection_021.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiB8__0V0NXefByIgUkONMRP4ZOaYKsFNQsNyGNBZncGRnad_qTekj76cXYGc59oY5AtZ3USJcHKwvNJWb_S-9vLJVIjp6XxmcDIq1N93W3aysibRt0smcHyho_3ri6vKjhlQVhGw/s1600/Selection_021.png&quot; height=&quot;251&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The takeaway : for the kids who want a little challenge at home, I asked them to practice drawing a Minecraft Creeper, which very roughly, looks like this - it will give the kids a chance to practice some of the skills that they&#39;ve already picked up (e.g. drawing squares), but also give a chance to learn a few new things as well (using background and fill colors)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;img src=&quot;https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcSSO91-mOyaRwqYnml2HZ6KVaR1k9TcROEqxUk_7SsPTk8qFjxwzQ&quot; /&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h3 style=&quot;text-align: center;&quot;&gt;
Setting up &lt;a href=&quot;http://www.kogics.net/kojo&quot;&gt;Kojo&lt;/a&gt;&lt;/h3&gt;
Kojo is a very kid-friendly piece of software which was created specifically to help children learn about programming and have fun along the way. It should run on all modern OS-s (Windows, Linux, MacOS), so as long as you have a desktop computer to install this on (but it will not work on a tablet), your child should be able to continue exploring it on their own. It contains a rich library of examples written in Kojo (which we will lean on in the class) - it even has a game or two that the kids can play with.&lt;br /&gt;
&lt;br /&gt;
Setting up Kojo is pretty straighforward:&lt;br /&gt;
1. &lt;a href=&quot;http://www.kogics.net/kojo-download&quot;&gt;Dowload Kojo&lt;/a&gt; from&amp;nbsp;http://www.kogics.net/kojo-download&lt;br /&gt;
2. Run the downloaded .jar file (e.g. by right clicking and choosing &quot;Run&quot; on windows), it will launch the installer - just follow through the installer and you should have an icon on your desktop that you can use to launch the software.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/6737429199803600183/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2014/03/intro-to-programming-first-class.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/6737429199803600183'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/6737429199803600183'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2014/03/intro-to-programming-first-class.html' title='Intro to Programming : The First Class'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiB8__0V0NXefByIgUkONMRP4ZOaYKsFNQsNyGNBZncGRnad_qTekj76cXYGc59oY5AtZ3USJcHKwvNJWb_S-9vLJVIjp6XxmcDIq1N93W3aysibRt0smcHyho_3ri6vKjhlQVhGw/s72-c/Selection_021.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-9084109715755305007</id><published>2012-01-16T22:07:00.000-05:00</published><updated>2012-01-16T22:07:09.004-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="jdo"/><category scheme="http://www.blogger.com/atom/ns#" term="tapestry"/><category scheme="http://www.blogger.com/atom/ns#" term="tynamo"/><title type='text'>Tynamo committer, new Tynamo module:tapestry-jdo</title><content type='html'>&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;The Announcement:&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
First of all the announcement : as a new commiter to &lt;a href=&quot;http://tynamo.org/&quot;&gt;Tynamo&lt;/a&gt;, I have just released a new module, &lt;a href=&quot;http://docs.codehaus.org/display/TYNAMO/tapestry-jdo+guide&quot;&gt;Tapestry-JDO&lt;/a&gt;, which allows Tapestry users to build applications based on JDO using familiar (from Hibernate and JPA integrations) Tapestry idioms for persistence manager injection and transaction management (using @CommitAfter). The official announcement is on the &lt;a href=&quot;http://docs.codehaus.org/display/TYNAMO/2012/01/16/Tapestry-jdo+0.0.1+released&quot;&gt;Tynamo Blog&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;&lt;b&gt;The History&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
How that came about...&lt;br /&gt;
&lt;br /&gt;
I&#39;ve been a Tapestry user for a few years, usually lurking on the mailing lists and answering a question here and there , wherever I could contribute. I&#39;ve always enjoyed Tapestry&#39;s approach to building web apps : pages, components, and annotation driven magic has been quite enjoyable, and the out of the box live class reloading make the experience pleasant and productive. At first I somewhat loathed the heavy emphasis on annotations to drive the framework magic; however, having dealt w/ the Grails&#39; way of doing it for a few years, it now seems a bit &amp;nbsp;preferable for being more clear and explicit.&lt;br /&gt;
&lt;br /&gt;
Fast forward a few years, Google announced the &lt;a href=&quot;http://appengine.google.com/&quot;&gt;App Engine&lt;/a&gt;&amp;nbsp;(GAE) and I was totally blown away of how they turned the world of java web app hosting on its head. Prior to GAE, Java web app hosting was both expensive and inconvenient - who would want to build publicly facing java apps, no wonder PHP ruled the roost . Of the two persistence APIs on the App Engine, JDO was the more familiar one, having spent a few years on the job working w/ JDO. Prior to GAE&#39;s choice to use JDO as a persistence API (at least in my world) it seemed like a dead end. Hibernate and JPA had taken the world by storm, and nobody seemed to care about JDO any longer.&amp;nbsp;Another reason why JDO seemed interesting on the App Engine is that from its very inception JDO claimed that it was not necessarily married to relational databases. While back in the day that seemed to me like a not-so-interesting design goal, when GAE chose it as one of the options it seemed to make so much more sense to use that, as the backend store of GAE is non relational. Today, with the rise of &amp;nbsp;NoSQL, it really starts clicking (e.g. &lt;a href=&quot;http://www.datanucleus.org/products/accessplatform/mongodb/support.html&quot;&gt;DataNucleus MongoDB &amp;nbsp;support&lt;/a&gt;) - all of a sudden, the same skillset that you&#39;ve developed from the relational ORM days, you can apply in non-relational contexts - sweet !&lt;br /&gt;
&lt;br /&gt;
So, &amp;nbsp;along the way I built a couple of apps to run on the App Engine - a site for &amp;nbsp;&lt;a href=&quot;http://www.zadachite.com/&quot;&gt;math problem solving assistance service&lt;/a&gt;&amp;nbsp;and &lt;a href=&quot;http://portfolio.anniesartbook.com/&quot;&gt;an artwork portfolio site&lt;/a&gt; . Neither of the apps were particularly complicated or difficult to build from a development point of view, but had just enough interaction w/ the back end where I had to deal w/ JDO directly - e.g. getting persistence managers, starting sessions, committing, all that. As much as I find &lt;a href=&quot;http://www.springframework.org/&quot;&gt;Spring&lt;/a&gt; to be a useful tool in the toolbox, booting up a full blown spring container just too much for a simple web app. Additionally, the existence of tapestry-ioc makes Spring redundant and way too verbose for many of its possible usages within a simple web app. On the other hand, without Spring, manual transaction management is a royal PITA and I was looking for a better of way doing it. My first stab was to set up a Spring &amp;nbsp;dependency (w/o a spring container) to just get the Transaction manager classes and wire them up in Tapestry IOC. It worked pretty decently but the integration was just a kludge, not to mention the few megabytes that the four classes from Spring I used brought down with them.&lt;br /&gt;
&lt;br /&gt;
Having been on the Tapestry mailing lists, the &lt;a href=&quot;http://tynamo.org/&quot;&gt;Tynamo project&lt;/a&gt; has continued to impress me with the suite of modules that the project kept releasing to fill needs in the tapestry community - e.g. jpa, security, routing, openid authentication, and more. Thus, when I started wondering where to look for an example of how to build the JDO support, Tynamo was the first candidate.&amp;nbsp;So, I grabbed &amp;nbsp;&lt;a href=&quot;http://tynamo.org/tapestry-jpa+guide&quot;&gt;Tynamo-JPA&lt;/a&gt;, moved some things around replaced all the usages of the JPA Entity Manager w/ JDO&#39;s PersistenceManager, touched every piece of code in the module and before you know it I had a working basic setup that allowed me to inject a PersistenceManager and use a @CommitAfter annotation to set up some automatic transaction management for myself. With that basic implementation I was able to finish the project that I was working on and everything looked great.&lt;br /&gt;
&lt;br /&gt;
Then, came the question of what to do w/ my changes. It seemed that something like this might be a valuable contribution to the Tapestry community, yet I didn&#39;t quite feel like setting up a project somewhere . So, I figured, since I heavily borrowed from Tynamo in the implementation, why not ask them if they would be interested in having the module be a part of their project. On first thought it seemed like a long shot, my scrappy JDO module couldn&#39;t possibly fit within the pure awesomeness of Tynamo. Yet, I figured, it&#39;s worth a shot.&amp;nbsp;&amp;nbsp;Shortly after I asked if they wanted it, they said yes, I zipped up the source and sent it out. I didn&#39;t necessarily plan on becoming a committer on the project, but before I knew it , Kalle had added me to the list and given me access to SVN, and posted an announcement on the blog about a new member of the project. I was a bit surprised by the warm welcome I received - I certainly felt that my contribution was appreciated.&lt;br /&gt;
&lt;br /&gt;
To make the long story short, a few months later, major code and javadoc cleanup, fixing the tests and stepping through the Tynamo / Codehaus release process, the cat is out of the bag - the module is released, the announcements are out. I hope this new module is useful to someone. &lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-size: large;&quot;&gt;Enjoy !&amp;nbsp;&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/9084109715755305007/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2012/01/tynamo-committer-new-tynamo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/9084109715755305007'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/9084109715755305007'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2012/01/tynamo-committer-new-tynamo.html' title='Tynamo committer, new Tynamo module:tapestry-jdo'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-4395165960119500054</id><published>2011-12-01T09:36:00.001-05:00</published><updated>2011-12-01T22:34:16.473-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="selenium"/><category scheme="http://www.blogger.com/atom/ns#" term="testing"/><category scheme="http://www.blogger.com/atom/ns#" term="ui-elements"/><title type='text'>Ad-Hockery functional testing : UI Elements and Selenium experience</title><content type='html'>This post is a response to &lt;a href=&quot;http://adhockery.blogspot.com/2011/11/fear-loathing-in-functional-testing.html&quot;&gt;Rob&#39;s posting on functional testing &lt;/a&gt;that just became too long to post in a comment...&lt;br /&gt;
&lt;br /&gt;
-----------&lt;br /&gt;
At my job, we&#39;ve used the UI elements approach pretty extensively and at this time we&#39;re moving away from it on a new project. &lt;br /&gt;
&lt;br /&gt;
Conceptually, I&#39;ve also enjoyed the idea that the location logic is sitting in the same tier as the elements that are being located. The support in S-IDE is also quite nice and impressive (e.g. code completion, documentation), the built-in support for unit testing the locators is quite nice. &lt;br /&gt;
&lt;br /&gt;
Now, the downsides....&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;UI Elements development / future&lt;/b&gt; : although it is technically a part of Selenium, in practice UI Elements support don&#39;t actually evolve in lockstep with it. On a conference call/presentation w/ one of Selenium&#39;s maintainers a few months ago, when I asked about UI elements, he indicated that they might be moved out of the core (he didn&#39;t seem to be particularly fond of them, I almost had the feeling that he was tempted to just drop the feature). There isn&#39;t much of a community using them in the first place, thus if something breaks they can remain broken for a while until someone notices (when that happened to us, the turnaround was pretty quick; although I&#39;m not sure if that is the way it is or because the author of UI elements for Selenium was a former employee). &lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;The skills gap&lt;/b&gt; : the fact that the UI element definitions are defined outside the main &quot;test case authoring&quot; environment (which in our case is Groovy), makes it another distinct skill that team members (often QA folks) need to acquire. When it comes to Javascript, being a user is easy, being an author who can sling Javascript and write nifty unit tests (typically not a QA skillset) inside of the UI elements is not that easy. At least in our case it led to a bunch of not very well maintained UI Elements, w/ most of the Unit tests commented out and the knowledge of writing UI Elements withing the QA team considered almost a Dark Art. As a result of all this&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Development cadence issues&lt;/b&gt; : although the usage of UI elements in a test case seems very sexy, the same &quot;development cadence&quot; for UI Elements is kinda slow (this could be my own ignorance as well). Basically, if you wanted to enhance a UI element, you would write some Javascript to set up the UI Element definitions and then close S-IDE and open it up again. If your unit test failed, then you would get a bunch of popups about the failing tests, the UI Elements would be disabled and you&#39;re back to editing the javascript file until the unit test passes. Only after that, you can try using the UI element in S-IDE or in your test case. Finally, if you were using selenium-server to run your &quot;real&quot; test cases (like we did), you need to restart selenium server (RC) so that it can pick up the updated UI Elements. &lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;UI Element organization&lt;/b&gt; : this could be an artifact of our own failings w/ UI elements and not necessarily a problem w/ the framework itself; however, the lack of a well defined approach to structuring them has led us into a massive set of pagesets that contains UI elements for a whole bunch of different pages. Thus, when you try to pick up a UI element there isn&#39;t a good organizing principle (&quot;Oh, yeah, I&#39;m on the Login page, lemme see what I can locate there&quot;) - instead we have something along the lines of (&quot;I&#39;m in feature &#39;foo&#39;, let me see which one of the several hundred UI element in the fooFeaturePageSet might fit&quot;). &lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Although the Page object approach is not a silver bullet it addresses a bunch of the issues above:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Skillset&lt;/b&gt; - the same skills that test authors use to write test cases, they use to write the abstraction (page objects)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;A clear organizing principle&lt;/b&gt; - people get it, pages have stuff in them, putting the location logic for an element on a page inside of the corresponding page object makes sense. Then, using that is just a method call&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Deployment / Cadence issues&lt;/b&gt; - there is no longer the impedance mismatch of &quot;I want to run this test case, did I re-deploy the UI elements that it depends on?&quot;. You just run the test, the testing framework will recompile the modified page objects and the test case runs, no need to do anything w/ Selenium server or anything else. &lt;/li&gt;
&lt;/ul&gt;
Anyway, just my 2c on the subject.</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/4395165960119500054/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2011/12/ad-hockery-functional-testing-ui.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/4395165960119500054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/4395165960119500054'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2011/12/ad-hockery-functional-testing-ui.html' title='Ad-Hockery functional testing : UI Elements and Selenium experience'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-4227547051354256945</id><published>2011-11-30T18:00:00.001-05:00</published><updated>2011-12-01T00:52:36.767-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="scala"/><category scheme="http://www.blogger.com/atom/ns#" term="tapestry"/><title type='text'>My Scala (and Tapestry 5) experience</title><content type='html'>Since  both a couple of coworkers&amp;nbsp;asked me about Scala in the last few weeks, I thought these might be interesting to read through (something that emerged over the last few days)&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&amp;nbsp;http://codahale.com/downloads/email-to-donald.txt&lt;/li&gt;
&lt;li&gt;&amp;nbsp;http://codahale.com/the-rest-of-the-story/&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;br /&gt;
I have obviously not used Scala to the extent that the guy describes, but I can testify at how annoying it is the constant translation between Java and Scala (especially, since &lt;a href=&quot;http://portfolio.anniesartbook.com/&quot;&gt;my small app&lt;/a&gt;, ,  was using &lt;a href=&quot;http://tapestry.apache.org/&quot;&gt;Tapestry 5&lt;/a&gt;, which is obviously a Java framework). The conversion between the two was made worse (at least for me, a total Scala newb) by the following:&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Classes with the same names but completely or subtly different usages and intents .&lt;/b&gt; The fact that the Scala library used the same class names as the Java library (java.lang.Long and scala.Long) , the default imports use them, and there are some magical conversions that occur between the eponymous types. On a few occasions I was totally baffled about something not working only to find that in the end, the code was getting the wrong type . Thus, in a bunch of my classes, I ended up explicitly having to import the Java classes that the framework expected to work with , e.g.&amp;nbsp;
&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;  
     import java.util.{List =&amp;gt; JList }
     import org.slf4j.Logger
     import scala.collection.JavaConversions._

     class FooPage {


 @Property
 private var catPieces:JList[ArtPiece] = _


        // explicitly declaring the returning types as the Java types
 def onPassivate():JList[String]= {
                // and using the Scala provided conversions
  return seqAsJavaList(List(category,subCategory))
 }

     }
      
&lt;/pre&gt;
and then explicitly use the java specific types. A similar but different situation exists with java.util.List and the scala List classes (e.g. &amp;nbsp;&lt;a href=&quot;http://www.scala-lang.org/api/current/scala/collection/immutable/List.html&quot;&gt;scala.collections.immutable.List&lt;/a&gt;) although they have the same name they have a completely different purpose (e.g. the Scala list is not necessarily intended to be created, and manipulated like the Java list); the equivalent of the java list is the recommended Scala &lt;a href=&quot;http://www.scala-lang.org/api/current/scala/collection/mutable/ListBuffer.html&quot;&gt;ListBuffer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Null handling&lt;/b&gt; - &amp;nbsp;because I was interacting w/ a Java framework, there was an expectation that nulls are OK and at different places, the framework does expect methods to return nulls in order to behave in certain ways. Scala goes for the whole &lt;a href=&quot;http://www.codecommit.com/blog/scala/the-option-pattern&quot;&gt;Option pattern&lt;/a&gt; (where you aren&#39;t supposed to use nulls to make it all better) and has some conversion (that I obviously, don&#39;t fully understand) between null and these types. However, because of the interaction w/ the Java framework, I had to learn how to deal with both. It kinda sucked.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Tapestry 5 and Scala interactions&lt;/b&gt; - &amp;nbsp;because Tapestry 5 pushes the envelope on being a Java framework w/ a whole bunch of annotation processing, class transformations, etc. , in &lt;a href=&quot;http://fanf42.blogspot.com/2009/03/java-annotation-scala-object-and.html&quot;&gt;some cases there were clashes between the T5 approach and Scala&lt;/a&gt;. In some respects, Tapestry 5 manages to be a respectable and succinct Java framework by adding a whole bunch of metaprogramming features, which when used with Scala make the scala code less attractive, e.g:&amp;nbsp;&lt;/li&gt;
&lt;ul&gt;
&lt;li&gt;Page properties that would otherwise be set up as private fields in regular Tapestry 5, now have to be declared as private fields and initialized. If you didn&#39;t declare them as private, then T5 would complain (since pages can&#39;t have non-private members as it is managed by T5) , e.g. :

&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;  
  class Foo {      
        @Inject 
 var logger:Logger = _
   
 @Inject 
 var pm:PersistenceManager = _
  }   
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Sometimes the T5 and Scala approaches seemed to clash in ways that make things complicated. For example, in the persistent objects in the class, I often annotated the private fields w/ @BeanProperty (so that Scala generates proper getters/setters for those fields). 
&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;  
  import scala.reflect.BeanProperty
  import javax.jdo.annotations.Persistent;
  class PersistentFoo {      
        @BeanProperty 
 @Persistent
 var title = &quot;&quot;

  }   
&lt;/pre&gt;
Yet, when I accidentally did the same for some page properties at weird points the application would start failing (on application reload with Tapestry&#39;s live class reloading) until in pages I replaced the approach w/ Tapestry&#39;s @Property annotation (although they&#39;re supposed to do the same it&#39;s quirky w/ BeanProperty)
&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;  
  import org.apache.tapestry5.annotations.Property; 

  class FooPage {      
        @Property 
 private var category:String = _

  }   
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;br /&gt;
When I was working on the app, a few times I had to just stop for a day because I couldn&#39;t figure out how to do something massively simple (e.g. how to succinctly join a list of Strings into a comma separated string - stuff that would have taken me 30 seconds to do in Java, 2 seconds in Groovy and the proposed Scala solutions seemed like massive overkill).&amp;nbsp;I originally started wanting to write some tests in Scala for this app, because I thought, &quot;wouldn&#39;t it be nice to have something a little more flexible and less verbose than Java&quot;, but that still has nice static typing. Later I decided to try the whole Scala+T5 approach, and I have to admit I was pretty mad at myself when I would get stuck .&lt;br /&gt;
&lt;br /&gt;
Obviously, many of my problems described above were due to my own weak Scala-foo (e.g. I had read through at least 2-3 books in order to be brave enough to try this just to learn that until I try things hands on, it doesn&#39;t stick too well), and other issues that I had were due to the interaction w/ the specific Java framework that I chose (Tapestry 5). Yet, in some ways, the experience was somewhat disappointing - having worked w/ Groovy for the last few years there is a massive difference in the approaches of the two languages. Where Groovy would often sacrifice some &quot;internal beauty&quot; in order to make a Java developer&#39;s life sweet and pleasant, e.g. :&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt; Joining a list of strings
&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt; 
   [1,2,3].join(&quot;,&quot;);
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;string formatting using $ inside of strings 
&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt; 
   &quot;Blah blah $fooVar&quot;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Null safe dereference 
&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt; 
   foo?.bar
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
... whereas Scala somehow gets stuck in an ideological mode e.g.&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://blog.aggregat4.net/a-stringjoinlist-for-scala-using-reduceleft&quot;&gt;Functional programming purism&lt;/a&gt;&amp;nbsp;, or&amp;nbsp;
&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;   List(1,2,3).reduceLeft(_ + &quot;,&quot; + _) 
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href=&quot;http://stackoverflow.com/questions/1163393/best-scala-imitation-of-groovys-safe-dereference-operator&quot;&gt;bizarre nullsafe dereference comments on StackOverflow&lt;/a&gt;

&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;def ?[A](block: =&amp;gt; A) =
  try { block } catch {
    case e: NullPointerException if e.getStackTrace()(2).getMethodName == &quot;$qmark&quot; =&amp;gt; null
    case e =&amp;gt; throw e
  }
&lt;/pre&gt;
... and usage ...
&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;   val a = ?(b.c.d.e)
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;br /&gt;
One part of my setup that worked very &amp;nbsp;well and I enjoyed quite a bit was the Continuous Compilation and&amp;nbsp;&lt;a href=&quot;http://tapestry.apache.org/class-reloading.html&quot;&gt;Tapestry&#39;s Live Class Reloading&lt;/a&gt;. Whereas for prior Tapestry pure-Java projects I had to rely on IDE magic to do some Compile-on-Save so that Tapestry can reload the changed classes, w/ the Scala setup it was much nicer. &amp;nbsp;I set up a Maven project w/ the&amp;nbsp;&lt;a href=&quot;http://scala-tools.org/mvnsites/maven-scala-plugin/&quot;&gt;Scala Maven plugin&lt;/a&gt;&amp;nbsp;, and then kick off the&amp;nbsp;&lt;a href=&quot;http://scala-tools.org/mvnsites/maven-scala-plugin/example_cc.html&quot;&gt;scala:cc goal&amp;nbsp;&lt;/a&gt;&amp;nbsp;to make it compile the changed page classes into my project. Thus, I had a completely IDE-independent setup that gave me a live-reloading experience on-par (and possibly beyond) the reloading experience with Grails.&lt;br /&gt;
&lt;br /&gt;
In the end, after I managed to work through some of the issues described above, it ended up being a pretty reasonable set up and I was able to make pretty decent progress in getting the app out the door (for my wife&#39;s birthday). At the same time, I wasn&#39;t really able to leverage any cool Scala features that would magically boost my productivity, or make the codebase significantly cleaner or smaller (in some respects, it feels like the Scala based code is more verbose because of all the conversions and casting into Java types). I feel that if I knew more about Scala and was more knowledgeable about Tapestry internals, I might be able to write a Tapestr5 - Scala adapter layer that would plug into some of T5&#39;s extensions points to make it work more naturally with Tapestry (e.g. working w/ Scala lists in views, different handling of null values, etc). As a learning experience - I learned a lot, both about things that were interesting and useful (a bit of functional programming, Java/Scala integration), and some things that I really didn&#39;t want to know that much about (how Scala and T5 munge the Java classes to make the things tick).&lt;br /&gt;
&lt;br /&gt;
In any event, the advice to people who like to try this kind of integration would be to allow yourself plenty of time for learning and experimentation w/ Scala and not giving up too early ( as I was almost ready to do on a few occasions). &lt;a href=&quot;http://fanf42.blogspot.com/2009/01/tapestry-5-with-scala-get-code.html&quot;&gt;Fanf&#39;s blog has a few blog entries and a project on GitHub&lt;/a&gt; that is &amp;nbsp;an excellent starting point.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/4227547051354256945/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2011/11/my-scala-and-tapestry-5-experience.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/4227547051354256945'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/4227547051354256945'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2011/11/my-scala-and-tapestry-5-experience.html' title='My Scala (and Tapestry 5) experience'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-4977554823241151361</id><published>2011-06-15T01:05:00.002-04:00</published><updated>2011-06-15T07:32:36.841-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="grails"/><category scheme="http://www.blogger.com/atom/ns#" term="web flow"/><title type='text'>Grails, Web Flows, and redirecting out</title><content type='html'>If you read the &lt;a href=&#39;http://grails.org/doc/1.3.3/guide/6.%20The%20Web%20Layer.html#6.5.1
Start and End States&#39;&gt;Grails Web Flow documentation&lt;/a&gt; it all seems pretty straightforward - start,end states, transitions, actions, events, it&#39;s all good. However, just like any other technology that seems like magic, there always is some kind of a catch once you start using it. &lt;br /&gt;
&lt;br /&gt;
One of the little &#39;gotchas&#39; that I ran into was how to properly complete the flow. Now, reading up the documentation, it would seem easy - at the end of your wizard/flow, you just redirect to a different controller+action and it&#39;s all good. It all makes sense - often time, the wizard walks through multiple steps, collects some information, and when it&#39;s all done (you save your brand new Foo), you can just redirect to the details page for foo (e.g. /foo/show/1). &lt;br /&gt;
&lt;br /&gt;
Well you thought it would be that easy. Not that quick... &lt;br /&gt;
&lt;br /&gt;
The Grails web flow documentation is kinda deceptive like that. It shows you a simplistic example that works; however, when you try to do something more realistic, then you start getting into trouble. So, the example form the docs looks like this:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;  def fooFlow = { 
       def startState { }

       def endState {
             redirect(controller:&#39;foo&#39;, action:&#39;bar&#39;)
       }

  }
&lt;/pre&gt;&lt;br /&gt;
The catch is that in their examples, the redirect is to a static URL that doesn&#39;t take any parameters.The problem comes up when in the end state you try to pass in some parameter from the flow (e.g. which often is the case e.g. at the end of the flow you want to display the details page (e.g. redirect(controller:&#39;foo&#39;, action:&#39;bar&#39;, id:flow.fooId) . The problem manifests itself in a weird way - the web flow would store a particular value of flow property( e.g. flow.fooId (under  onditions that I couldn&#39;t figure out), and even though your current wizard might have stored a particular value from the current flow, for whatever reason it ends up redirecting to a value stored from a previous flow. So, the wizard &#39;kinda&#39; worked in that it redirected to a details page at the end of the wizard, but in a large percentage of the time, it would redirect to the wrong details page. From what I could gather, the issue is that in the end state, the redirect cannot use any values from the flow, session, or flash and as a result uses some cached value (possibly from the first flow execution)&lt;br /&gt;
&lt;br /&gt;
The solution to this (which is somewhere on the Grails mailing lists) is as follows:  add an explicit empty &quot;end&quot; state (including an empty gsp to match the end state name), and in the states that transition into the end flow, issue the redirect from the penultimate state, e.g.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;
   def fooFlow = {
       def startState { }

       def beforeEnd {
             action {
                  redirect(controller:&#39;foo&#39;, action:&#39;bar&#39;, id:flow.fooId)
             }
             on(&quot;success&quot;).to &quot;end&quot;
       }

       def end( 
          /** note that this requries an end.gsp to be present
              in the flow subdirectory, but it never gets rendered after the
              redirect **/
       )
   }
&lt;/pre&gt;&lt;br /&gt;
Now, with this trick at hand, the end.gsp never gets rendered, and the client browsers do get redirected to the detail pages that you want to display, outside of your web flow. &lt;br /&gt;
&lt;br /&gt;
As a more Web Flow centric alternative, you could always store the relevant object (Foo) inside the flow and display any relevant details about the object in the end state (end.gsp)</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/4977554823241151361/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2011/06/grails-web-flows-and-redirecting-out.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/4977554823241151361'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/4977554823241151361'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2011/06/grails-web-flows-and-redirecting-out.html' title='Grails, Web Flows, and redirecting out'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-6448427120712199018</id><published>2011-04-03T01:09:00.006-04:00</published><updated>2011-04-08T01:11:53.465-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="netbeans"/><category scheme="http://www.blogger.com/atom/ns#" term="netbeans rcp"/><title type='text'>NetBeans Database Explorer API and databases in NetBeans</title><content type='html'>At the recent &lt;a href=&quot;http://edu.netbeans.org/courses/nbplatform-certified-training/&quot;&gt; NetBeans Platform Certified Training&lt;/a&gt; organized by &lt;a href=&quot;http://visitrend.com/news.htm&quot;&gt;Visitrend&lt;/a&gt; we were discussing how to work w/ the built in database functionality. NetBeans ships w/ a pretty decent database/SQL functionality out of the box - you can connect to any JDBC compliant database, add your drivers, sling queries, edit the results. It even has code completion for SQL queries - up until SQL Server 2005 it even provided better support for SQL code authoring than the built in Management tools ( and it is still better than the standard Mysql console). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, it turns out that for data driven applications, it is a pretty common occurrence that the users need to connect to a database. Sometimes it makes sense to hide the details of the database; at the same time, when you&#39;re dealing with sophisticated that have intimate knowledge of the underlying database schema and need to be able to deal with the underlying data, hiding the fact that they&#39;re dealing with a database just doesn&#39;t make sense. In our case, we have a team of QA Engineers who need to be able to look into all aspects of the database behind the application, so the best a tool can do is to make the setup and access to the database as easy and transparent as possible. &lt;br /&gt;
&lt;br /&gt;
Thus, to solve this problem, my users need the following : &lt;br /&gt;
1. Make sure that the IDE has the proper drivers set up to access our test databases&lt;br /&gt;
2. Easy setup of the database connection with the details for a specific project/system under tes&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Automatically setting up JDBC driver&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Unfortunately, Microsoft&#39;s SQL Server is not one of the JDBC drivers that ships with the IDE. A new user could just navigate to the &quot;Drivers&quot; node in the &quot;Services&quot; top component and walk through the wizard to register a new driver. This can certainly be an extra step in the list of &quot;setup instructions&quot;. But, why should a user have to remember to do that if we can do it in a module. Thus, the first hurdle we need to overcome is have a module that automatically registers the JDBC driver in the IDE :&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;1. First, we need to&amp;nbsp; provide the MSSQL JDBC driver. &lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
For that, I created a new Library Wrapper module. I wanted to mention this because for whatever reason when I tried providing the JDBC driver and the XML registration below in the same module, it failed to find the JDBC driver.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidKPiyB9veDOZzxBQZnEpZh_t6cbKDFQRRCJ3VxmqT4UYuaHMf8OXPh7HzLGoTS0qWw200x-N_RixbAMa8z80y04dXutF3yRk5Jy9Bqxjf6EqtbcIZ7GgC7n_4ExJGO41djM25RQ/s1600/nb_sql_jdbc.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidKPiyB9veDOZzxBQZnEpZh_t6cbKDFQRRCJ3VxmqT4UYuaHMf8OXPh7HzLGoTS0qWw200x-N_RixbAMa8z80y04dXutF3yRk5Jy9Bqxjf6EqtbcIZ7GgC7n_4ExJGO41djM25RQ/s1600/nb_sql_jdbc.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
In the general case for a pure JDBC driver, this should be enough. However, in order to support windows authentication, the JDBC driver needs to have a&amp;nbsp;&amp;nbsp; dll included on the path. In order to sutpport jars that need native libraries, the native library needs to be placed in the release/modules/lib as indicated on &lt;a href=&quot;http://bits.netbeans.org/dev/javadoc/org-openide-modules/org/openide/modules/doc-files/api.html#jni&quot;&gt;Modules API Javadoc&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b style=&quot;clear: both;&quot;&gt;2. Create a second module for the actual driver registration. &lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Add an XML descriptor for registering new drivers (named SQLServer2008Driver.xml). For MSSQL it looks like this :&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;xml&quot; name=&quot;code&quot;&gt;&amp;lt;?xml version=&amp;#39;1.0&amp;#39;?&amp;gt;
&amp;lt;!DOCTYPE driver PUBLIC &amp;#39;-//NetBeans//DTD JDBC Driver 1.1//EN&amp;#39; &amp;#39;http://www.netbeans.org/dtds/jdbc-driver-1_1.dtd&amp;#39;&amp;gt;
&amp;lt;driver&amp;gt;
  &amp;lt;name value=&amp;#39;SQLServer2008&amp;#39;/&amp;gt;
  &amp;lt;display-name value=&amp;#39;Microsoft SQL Server 2008&amp;#39;/&amp;gt;
  &amp;lt;class value=&amp;#39;com.microsoft.sqlserver.jdbc.SQLServerDriver&amp;#39;/&amp;gt;
  &amp;lt;urls&amp;gt;
      &amp;lt;url value=&amp;quot;nbinst:/modules/ext/sqljdbc4.jar&amp;quot;/&amp;gt;
  &amp;lt;/urls&amp;gt;
&amp;lt;/driver&amp;gt;



&lt;/pre&gt;&lt;br /&gt;
I am not entirely sure of the meaning of the nbinst: prefix, but this works.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;3. Add an entry into the layer.xml file of the second module to add the driver registration XML :&amp;nbsp;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;xml&quot; name=&quot;code&quot;&gt;&lt;filesystem&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;folder name=&quot;Databases&quot;&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;folder name=&quot;JDBCDrivers&quot;&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;file name=&quot;SQLServer2008Driver.xml&quot; url=&quot;SQLServer2008Driver.xml&quot;&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/file&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/folder&gt;&lt;/folder&gt;
&lt;/filesystem&gt;
&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Unfortunately, in the case of the MSSQL JDBC driver just adding the dll into the module doesn&#39;t cut it as it appears that the SQL Server driver also depends on other DLLs, so it actually needs to be in c:\windows\system32 . Thus, adding the jdbc driver dll would have been not needed, as the dll needs to be copied to windows\system32 directory. To do that, register a module installer&amp;nbsp; :&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;public class MssqlDriverInstaller extends ModuleInstall {

&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override
&amp;nbsp;&amp;nbsp;&amp;nbsp; public void restored() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; File mssqlJdbcDll = new File(System.getenv(&quot;windir&quot;), &quot;system32\\sqljdbc_auth.dll&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; boolean foundDll = mssqlJdbcDll.exists();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (!foundDll) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; FileOutputStream system32MssqlDll = null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; InputStream bundledDll = null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; system32MssqlDll = new FileOutputStream(mssqlJdbcDll);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; bundledDll = MssqlDriverInstaller.class.getResourceAsStream(&quot;sqljdbc_auth.dll&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.out.println(&quot;Copying sqljdbc_auth.dll to windows system32&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; FileUtil.copy(bundledDll, system32MssqlDll);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } catch (IOException ex) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Logger.getLogger(MssqlDriverInstaller.class.getName()).log(Level.SEVERE, null, ex);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } finally {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; system32MssqlDll.close();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } catch (IOException ex) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Logger.getLogger(MssqlDriverInstaller.class.getName()).log(Level.SEVERE, null, ex);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; bundledDll.close();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } catch (IOException ex) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Logger.getLogger(MssqlDriverInstaller.class.getName()).log(Level.SEVERE, null, ex);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}

&lt;/pre&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;Automatically connecting to the database&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;span style=&quot;font-size: small;&quot;&gt;Now, the last step is, how to automate the creation of the database connection node now that we can now be sure that the IDE has the right driver to connect to the database: (the APIs used below are from the &lt;a href=&#39;http://bits.netbeans.org/dev/javadoc/org-netbeans-modules-db/overview-summary.html&#39;&gt;Database Explorer API&lt;/a&gt;, org.netbeans.api.db.explorer) &amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-size: large;&quot;&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;pre class=&quot;java&quot; name=&quot;code&quot;&gt;public void createProjectConnection(DatabaseConfig dbc) {

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DatabaseConfig dbConfig = dbc;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (dbConfig == null) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dbConfig = DatabaseConfig.getDefault();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; JDBCDriver sqlSrvDrv = findSqlServerDriver();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (sqlSrvDrv != null) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DatabaseConnection dbConn = createDbConnection(dbConfig, sqlSrvDrv);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; final ConnectionManager connMgr = ConnectionManager.getDefault();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DatabaseConnection foundConn = findSameConnection(dbConn);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (foundConn == null) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foundConn = dbConn;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; connMgr.addConnection(dbConn);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; final DatabaseConnection dbConn2 = foundConn;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RequestProcessor.getDefault().post(new Runnable() {

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void run() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; try {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; connMgr.connect(dbConn2);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; connMgr.selectConnectionInExplorer(dbConn2);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } catch (DatabaseException ex) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Logger.getLogger(Ats3ProjectDataService.class.getName()).log(Level.SEVERE, &quot;Failed to connect to database&quot;, ex);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; });

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } catch (DatabaseException ex) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Logger.getLogger(Ats3ProjectDataService.class.getName()).log(Level.SEVERE, &quot;Failed to connect to database&quot;, ex);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp; private DatabaseConnection createDbConnection(DatabaseConfig dbConfig, JDBCDriver sqlSrvDrv) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DatabaseConnection dbConn;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String url = null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String userDb = dbConfig.getName();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (userDb != null) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (userDb.contains(&quot;${username}&quot;)) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; userDb = userDb.replace(&quot;${username}&quot;, System.getProperty(&quot;user.name&quot;));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } else {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; userDb = System.getProperty(&quot;user.name&quot;) + &quot;_sb_rc&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (!dbConfig.getUseSqlAuth()) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; url = String.format(&quot;jdbc:sqlserver://%s:1433;databaseName=%s;integratedSecurity=true&quot;, dbConfig.getServer(), userDb);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dbConn = DatabaseConnection.create(sqlSrvDrv, url, &quot;&quot;, &quot;dbo&quot;, &quot;&quot;, true);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } else {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; url = String.format(&quot;jdbc:sqlserver://%s:1433;databaseName=%s&quot;, dbConfig.getServer(), userDb);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; dbConn = DatabaseConnection.create(sqlSrvDrv, url, dbConfig.getUser(), &quot;dbo&quot;, dbConfig.getPassword(), true);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return dbConn;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp; private JDBCDriver findSqlServerDriver() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; JDBCDriver sqlSrvDrv = null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; JDBCDriver[] drivers = JDBCDriverManager.getDefault().getDrivers(&quot;com.microsoft.sqlserver.jdbc.SQLServerDriver&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // we know that there should be at least one as this module registers it
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (JDBCDriver drv : drivers) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (&quot;SQLServer2008&quot;.equals(drv.getName())) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; sqlSrvDrv = drv;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return sqlSrvDrv;
&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp; private DatabaseConnection findSameConnection(DatabaseConnection dbConn) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DatabaseConnection foundConn = null;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ConnectionManager connMgr = ConnectionManager.getDefault();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (DatabaseConnection dbc : connMgr.getConnections()) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (dbc.getDatabaseURL().equals(dbConn.getDatabaseURL())) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; foundConn = dbc;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return foundConn;
&amp;nbsp;&amp;nbsp;&amp;nbsp; } 
&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/6448427120712199018/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2011/04/programmatically-working-with-databases.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/6448427120712199018'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/6448427120712199018'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2011/04/programmatically-working-with-databases.html' title='NetBeans Database Explorer API and databases in NetBeans'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidKPiyB9veDOZzxBQZnEpZh_t6cbKDFQRRCJ3VxmqT4UYuaHMf8OXPh7HzLGoTS0qWw200x-N_RixbAMa8z80y04dXutF3yRk5Jy9Bqxjf6EqtbcIZ7GgC7n_4ExJGO41djM25RQ/s72-c/nb_sql_jdbc.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-8646939076079410617</id><published>2010-10-26T22:21:00.003-04:00</published><updated>2010-10-26T22:29:52.882-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="apple"/><category scheme="http://www.blogger.com/atom/ns#" term="java"/><category scheme="http://www.blogger.com/atom/ns#" term="oracle"/><title type='text'>Why Oracle should do something about the Apple fiasco</title><content type='html'>I very strongly dislike Apple and after this latest stunt even more so. Seriously.&lt;br /&gt;&lt;br /&gt;Sometimes, people wonder : why should Oracle do something about the situation when it&#39;s pretty obviously Apple&#39;s decision that lead to this. So, here I go:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;1. Apple has nothing to lose by not supporting Java on OSX.&lt;/span&gt; So, they might lose a few thousand (tens of thousands? maybe...) Apple laptop toting java developers. It&#39;s a drop in the sea of iPhone, iPad, and iPod sales. So, maybe the same number of devs might not buy the next OSX upgrade - whooptie doo... &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;2. Oracle owns Java.&lt;/span&gt; They have wanted to &quot;own&quot; Java for the longest time, they even bought a whole company (Sun), hardware and all, just to get their grubby hands on it. Now, they own it and when Apple drops the ball, it&#39;s now up to Oracle to step up to the plate and show what &quot;owning&quot; the platform really means to them. &lt;br /&gt;&lt;br /&gt;I&#39;m sure Adobe would like if Apple produced their own compatible Flash for OSX, but Apple doesn&#39;t - Adobe does. Why ? &#39;Cause they own the platform.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;3. Oracle has the most to lose from this development&lt;/span&gt; - if Java doesn&#39;t run on MacOS, then much of the wonderful cross-platform claims of Java become somewhat gimpy as now Java will not run on a fairly mainstream modern client  OS. Not very cool, especially since Java (based on Oracle&#39;s own statements) wants to be on the desktop. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;From a personal perspective, I could care less about Java on Apple (although I did sign the petition as well). Up until now, I would have recommended it to people acknowledging it being a decent OS. Going forward, despite my dislike for Microsoft, I&#39;d probably just recommend Windows 7. Not that this matters all that much, not too many people ask me. &lt;br /&gt;&lt;br /&gt;Finally,  why are people are asking Oracle to do something and not Apple ? Really, it makes so much more sense to appeal to the company that caused the problem in the first place. However, as much as Oracle has been sucking in their relationship with the community lately, they are light years away from Apple&#39;s blindness and deafness to the needs of their customers when those needs happen to be at odds with the cult leader&#39;s ideas (e.g. no flash on iphones yet despite ample evidence that it&#39;s a part of a modern web experience, even for the mobile web)</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/8646939076079410617/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2010/10/why-oracle-should-do-something-about.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/8646939076079410617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/8646939076079410617'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2010/10/why-oracle-should-do-something-about.html' title='Why Oracle should do something about the Apple fiasco'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-38113369331997513</id><published>2010-10-21T22:57:00.002-04:00</published><updated>2010-10-21T23:05:09.979-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="apple"/><category scheme="http://www.blogger.com/atom/ns#" term="java"/><title type='text'>Apple and Java Devs - enough is enough !</title><content type='html'>There was a line in the iRobot movie with Will Smith where he says &quot;Somehow &#39;I told you so&#39; just doesn&#39;t cut it...&quot; after the chick in the movie finally saw how her robots actually malfunctioned and tried taking over the world (which Will Smith had been telling her all along). &lt;br /&gt;&lt;br /&gt;Anyway, Apple has been on this trajectory for the longest time. Even since MANY years ago Jobs had promised that he would make the mac the best platform for Java developers, a whole bunch of java devs have been lugging macs to conferences trying to look cool and  consoling themselves with the thought that &quot;it&#39;s actually Unix underneath, and it&#39;s pretty, so it&#39;s perfect&quot;. All that despite that Apple has been dissing them for the longest time - I remember how at first it felt weird when Apple deprecated some quicktime java bindings, then came more and more rare java releases, then it took forever for Java 6 to come out, then the same paranioia moved into iphone, ipad, flash... the list goes on and on.. &lt;br /&gt;&lt;br /&gt;I do have an appreciation for the fervor with which Mac fans defend their platform of choice. I&#39;m the same with Linux . Sometimes I would jump through some hoops that I probably would not have to go through if I was working in Windows. However, if tomorrow Linus Torvalds ( or some other cult figure in the community who could make that decision) came out and said that Linux is not going to run Java anymore (for some real or imaginary philosophical reason), despite my love for so many of the characteristics and ideas behind Linux, and despite my strong dislike for Windows and Microsoft, I would just switch to Windows. Seriously, I earn my livelihood with and because of Java, and if my OS vendor told me that my OS is not going to support me in earning my living - guess what - I will be switching my OS vendor. &lt;br /&gt;&lt;br /&gt;Sure, there will probably be ways to continue to somehow run Java on Macs in the foreseeable future . Sooner or later Oracle probably come up with some Java version that somehow runs on a mac.  You could probably run some unofficial build (e.g. from OpenJDK) that kinda works, or maybe you could run a Windows or Linux VirtualBox image and run the Java inside of that. But come on - as a Java developer , how many times does one need to be told that he/she is not wanted and shown the door ? &lt;br /&gt;&lt;br /&gt;So, here comes my call for action (of course as a Linux fanboy_:-) ) . I hear that these days Macs run on Intel hardware anyway - whip out that copy of Ubuntu that you always said you&#39;d try and take it out for a spin. Or try Fedora - it&#39;s quite decent. Worst case scenario - I&#39;ve seen my better half use Windows 7 and it seems to be OK ( she doesn&#39;t curse it as much as XP ) - it does run Java, and if you&#39;ve ever dealt with technologies you&#39;re already used to some level of hassle.</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/38113369331997513/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2010/10/apple-and-java-devs-enough-is-enough.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/38113369331997513'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/38113369331997513'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2010/10/apple-and-java-devs-enough-is-enough.html' title='Apple and Java Devs - enough is enough !'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-4802053931097107447</id><published>2009-06-02T23:43:00.010-04:00</published><updated>2009-06-03T01:21:30.449-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="grails"/><category scheme="http://www.blogger.com/atom/ns#" term="groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="netbeans"/><title type='text'>Quickly running a single Grails test in NetBeans</title><content type='html'>&lt;p&gt;One thing in the current NetBeans Grails project is that out of the box, you can run all tests in the project from the project&#39;s context menu but you can&#39;t run individual tests. While that is all good and well when you only have a few tests, once the tests are piling up (or you just are interested in how a particular test works), it is much preferable to be able to execute a single test at a time. &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/AVvXsEh87Zs8gugVJMiknrXyX9C2dc2ytHAtpbN5EySY5nNYssidLTJuI_kKtu5FnAIxuUzcN91Yqi1zfw4LGhGpAua3zGTOxcZD0xB7QUf_z5lL_ZZ1BMIKtCBNX7ht_zMVV7I-MMuAow/s1600-h/run_single_test.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 313px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh87Zs8gugVJMiknrXyX9C2dc2ytHAtpbN5EySY5nNYssidLTJuI_kKtu5FnAIxuUzcN91Yqi1zfw4LGhGpAua3zGTOxcZD0xB7QUf_z5lL_ZZ1BMIKtCBNX7ht_zMVV7I-MMuAow/s320/run_single_test.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5342960076242026226&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;There is an &lt;a href=&quot;http://www.netbeans.org/issues/showvotes.cgi?issue_id=159833&quot;&gt;existing issue&lt;/a&gt; to address this (so, go and vote for the issue if it seems useful, would ya?), but in the meantime, you still need to run your individual tests, right ? So, what should you do ? &lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;The trick is to just use the &quot;Run Grails Command&quot; feature in NetBeans and make it easily accessible so that you can run the test in a heartbeat. &lt;br /&gt;&lt;br /&gt;Here&#39;s what the feature looks like: &lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt; &lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXaxOe7y9RnbMPEpOpz5VLtWSaU-8ks9F7qfW-HoCF-jWVrtzZEOckcQkZsMQURdIiF0pVcdSyLwDiipmQ3IS7pKQcVaI15IURHxpqORxG8cBr2zkK62lmnyjq7mP0cvXWJ2Njww/s1600-h/run_grails_command.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 192px; height: 320px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXaxOe7y9RnbMPEpOpz5VLtWSaU-8ks9F7qfW-HoCF-jWVrtzZEOckcQkZsMQURdIiF0pVcdSyLwDiipmQ3IS7pKQcVaI15IURHxpqORxG8cBr2zkK62lmnyjq7mP0cvXWJ2Njww/s320/run_grails_command.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5342964550503366738&quot; /&gt;&lt;/a&gt;&lt;br /&gt;Right click on the project and select the &quot;Run Grails Command...&quot; option&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li style=&quot;clear:both&quot;&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirNidVB3YKU3AldZLMbpLRAsDhVxVT40TcEHjpdHxryPqweXI7slAnV0SPhB2Zl8vAMwROT2AnVrF-EWGXb6IGO6sbkP51taSO_QU8AN0aVqdeTbFAA7T6TZJ8bDIaOtwtuLvAuw/s1600-h/run_single_command_dialog.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 262px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirNidVB3YKU3AldZLMbpLRAsDhVxVT40TcEHjpdHxryPqweXI7slAnV0SPhB2Zl8vAMwROT2AnVrF-EWGXb6IGO6sbkP51taSO_QU8AN0aVqdeTbFAA7T6TZJ8bDIaOtwtuLvAuw/s320/run_single_command_dialog.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5342964834593964130&quot; /&gt;&lt;/a&gt;&lt;br /&gt;In the dialog, type &quot;test-app&quot; in the Filter text box, and then type in &quot;TestName -unit&quot; for unit tests, or &quot;TestName -integration&quot; for integration tests. Note that here &quot;TestName&quot; is the test class name minus the &quot;Tests&quot; suffix for unit tests and minus the &quot;IntegrationTests&quot; for integration tests. &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p style=&quot;clear:both&quot;&gt;&lt;br /&gt;&lt;br /&gt;The good news is that NetBeans remembers the command options from the previous run (not the command though) and if you pull up the dialog you just have to type &quot;test-app&quot; and hit enter and it will execute your previous test class. &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/AVvXsEjv05m101eFG-7qMBJDZuhnbh05ltBXmnjLF5ritxW5wsd_xFVxI49FXGReCNUXhfwnJzAvjw0wUA7L6tygBFH62Uyovn01UzP4VA4TNFPUDJLBeoTkyhWeTR1bN-i0PfsC3fzLkA/s1600-h/map_grails_shortcut_run_cmd.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 222px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv05m101eFG-7qMBJDZuhnbh05ltBXmnjLF5ritxW5wsd_xFVxI49FXGReCNUXhfwnJzAvjw0wUA7L6tygBFH62Uyovn01UzP4VA4TNFPUDJLBeoTkyhWeTR1bN-i0PfsC3fzLkA/s320/map_grails_shortcut_run_cmd.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5342964837954740530&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p style=&quot;clear:both&quot;&gt;&lt;br /&gt;Thus, the only thing you have left it to make it easy to call the &quot;Execute Grails Command&quot; - so, we just need to map a shortcut. In order to do that, go to Tools-&gt;Options-&gt;Key Map tab. In the &quot;Search&quot; box, type &quot;grails&quot;, the &quot;Execute Grails Command&quot; action is there, click in the Shortcut column, type in your shortcut (I picked Ctrl+Alt-C) and hit OK. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;Now, running your test the first time involves Ctrl-Alt-C, type &quot;test-app&quot;, tab, type &quot;FooBar -unit&quot; and hitting enter. Kicking it off subsequently is Ctrl-Alt-C, type &quot;test-app&quot;, hit enter and your tests are running !!! &lt;br /&gt;&lt;br /&gt;BEAUTY !!!&lt;br /&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/4802053931097107447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2009/06/quickly-running-single-grails-test-in.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/4802053931097107447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/4802053931097107447'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2009/06/quickly-running-single-grails-test-in.html' title='Quickly running a single Grails test in NetBeans'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh87Zs8gugVJMiknrXyX9C2dc2ytHAtpbN5EySY5nNYssidLTJuI_kKtu5FnAIxuUzcN91Yqi1zfw4LGhGpAua3zGTOxcZD0xB7QUf_z5lL_ZZ1BMIKtCBNX7ht_zMVV7I-MMuAow/s72-c/run_single_test.png" height="72" width="72"/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-9175747913089605708</id><published>2009-05-13T03:10:00.014-04:00</published><updated>2009-05-24T02:16:31.050-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="netbeans"/><category scheme="http://www.blogger.com/atom/ns#" term="tapestry"/><title type='text'>RAD w/ Tapestry 5, NetBeans 6.7, Maven, and Jetty : Really !!!</title><content type='html'>&lt;p&gt;One of the major upsides of using Tapestry 5 is the much touted &lt;a href=&quot;http://tapestry.apache.org/tapestry5/guide/reload.html&quot;&gt;live class and template reloading&lt;/a&gt;. Up until recently, if you followed &lt;a href=&quot;http://www.troymaxventures.com/2008/05/tapestry5-netbeans-quickstart.html&quot;&gt;my previous post&lt;/a&gt; on working with Tapestry 5 and NetBeans, you probably ended w/ a workable solution, but still not ideal , as the live template and class reloading wasn&#39;t exactly working as expected. As a result, whenever you wanted to see the changes that you made in the live app (after running mvn jetty:run) you had to do the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mvn compile resources:resources&lt;br /&gt;.........&lt;br /&gt;-----------&lt;br /&gt;[INFO] BUILD SUCCESSFUL&lt;br /&gt;[INFO] ------------------------------------------------------------------------&lt;br /&gt;[INFO] Total time: 18 seconds&lt;br /&gt;[INFO] Finished at: Wed May 13 03:16:32 EDT 2009&lt;br /&gt;[INFO] Final Memory: 16M/71M&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;The issue here was that NetBeans (in 6.1 and prior) did not support CopyOnSave or CompileOnSave properly in Maven projects (it did for NetBeans native projects, so if you had set up a NetBeans native project w/ explicit jar dependencies, etc it would work fine). The effect of running the above command was to compile your changes, and copy the compiled classes and modified resources into your &amp;lt;outputDirectory&amp;gt; (typically target/classes) . So, the 18 seconds above are not exactly something to lose sleep over, but it&#39;s still not the same like having the immediate Grails(or Rails)-like immediate feedback loop (that is, &quot;Ctrl-S-&gt;Alt-Tab to browser-&gt;F5&quot;, which is &quot;Save-&gt;Switch to Browser-&gt;Refresh&quot;). &lt;br /&gt;&lt;br /&gt;In any case, help is on the way. &lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;In the most recent version of NetBeans (in the &lt;a href=&quot;http://bits.netbeans.org/download/trunk/nightly/latest/&quot;&gt;6.7 daily builds&lt;/a&gt; ), the issues w/ CopyOnSave support has been fixed (well, almost fixed, see &lt;a href=&quot;http://www.netbeans.org/issues/show_bug.cgi?id=152974&quot;&gt;the NetBeans IssuZilla issue&lt;/a&gt;), and now it transparently copies your modified resource files to target/[app-name]/WEB-INF/classes. Thus, with just a minor tweak, you can accomplish a Tapestry 5 Nirvana. &lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt; First, set up a new Maven project by File-&gt;New Project-&gt;Select Maven project type. Follow &lt;a href=&quot;http://www.troymaxventures.com/2008/05/tapestry5-netbeans-quickstart.html&quot;&gt;my previous instructions&lt;/a&gt; on creating the actual project. Just a side note, for some reason the latest production T5 version (5.1) doesn&#39;t show up on the list of available archetypes in NetBeans. &lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt; Running the app is easy, the default project comes w/ the Jetty plugin set up, so you can just run &quot;mvn jetty:run&quot; on the console. &lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Alternatively, map a custom Maven goal in NetBeans by right clicking on the project, going to Custom-Goals and mapping jetty:run . See the screenshots for some extra help&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtdKdcdcnQBrJ03nI0TXlp1EXv60Ycw8JYnYYug1JZSXtv0W246Clb_ckVThC4KmsTBgmeWQ-Ng8rvxkgyBSHnU3ExzRgZxeluWB9aZheEx5T4pyGgCnXhlLIjFcZLuvY_kFl1vQ/s1600-h/maven_custom_goal1.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 255px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtdKdcdcnQBrJ03nI0TXlp1EXv60Ycw8JYnYYug1JZSXtv0W246Clb_ckVThC4KmsTBgmeWQ-Ng8rvxkgyBSHnU3ExzRgZxeluWB9aZheEx5T4pyGgCnXhlLIjFcZLuvY_kFl1vQ/s320/maven_custom_goal1.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5339266548738160754&quot; /&gt;&lt;/a&gt;&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/AVvXsEhSsAhTbBt_iRZN257lF6DqyQ95t3wayYtcvFo9qLG0vVad8elMep6nY7__xOqxV9itCmubX9zY9FJrJuwu_zWouOBJ8MAe8y4T6ie3nIZT5N8zTOPWijMp64ZkOX6o6vMFLL5Tfg/s1600-h/maven_custom_goal2.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 213px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSsAhTbBt_iRZN257lF6DqyQ95t3wayYtcvFo9qLG0vVad8elMep6nY7__xOqxV9itCmubX9zY9FJrJuwu_zWouOBJ8MAe8y4T6ie3nIZT5N8zTOPWijMp64ZkOX6o6vMFLL5Tfg/s320/maven_custom_goal2.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5339266737022152530&quot; /&gt;&lt;/a&gt;&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/AVvXsEhWXvRPA3n0AgDA8b-Qw7DQ0ZIMvUNETrpOFfWgxmyUMxqShZ4XnhhAbVCEWDDIQ4S1SxkGjLf5__ggXPXpWg15Xo5J2HWX5PfyFWNo8gpehYTknYAcPepSluYuondc_trvq6kNxA/s1600-h/maven_custom_goal3.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 218px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWXvRPA3n0AgDA8b-Qw7DQ0ZIMvUNETrpOFfWgxmyUMxqShZ4XnhhAbVCEWDDIQ4S1SxkGjLf5__ggXPXpWg15Xo5J2HWX5PfyFWNo8gpehYTknYAcPepSluYuondc_trvq6kNxA/s320/maven_custom_goal3.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5339266981810999394&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li style=&quot;clear:both&quot;&gt;The default project setup comes with an Index page living in the web app context. Now that you ran Jetty, you should be able to just make changes to the template, and see them immediately. The secret here is that Jetty runs by default out of src/main/webapp, so T5 picks up the changes out of the box, no additional support by the IDE is needed. &lt;br /&gt;&lt;br /&gt;The problem here is that if you tried making changes to your page class (e.g. Index.java), they&#39;re not being picked up. Jetty runs from the classes in target/classes. The idea here is that we want to IDE to autocompile the changes, drop them into target/classes and have T5 pick up the new page classes. As mentioned at the beginning of the post, if you just ran the maven build again (e.g. mvn compiler:compile), but we need something better. &lt;br /&gt;&lt;br /&gt;OK, so, go to the project properties, go to the Build-Compile section. In the panel, select from the &quot;Compile on Save&quot; (COS) dropdown the &quot;for both application and test execution&quot;. &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/AVvXsEgMXsXp9xX0SuF7CkO9T713yvnz7TlF5quo_NrnLMem9cz6Cem6K9oPNp9DfLcZ6sSjAhg8znCRjDkayiyVOPDZvFty-YBnk21hwz6mnwwCwsdPiU6_y1XPo7ZVvJ_LqT1V8uoesg/s1600-h/deploy-on-save.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 229px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMXsXp9xX0SuF7CkO9T713yvnz7TlF5quo_NrnLMem9cz6Cem6K9oPNp9DfLcZ6sSjAhg8znCRjDkayiyVOPDZvFty-YBnk21hwz6mnwwCwsdPiU6_y1XPo7ZVvJ_LqT1V8uoesg/s320/deploy-on-save.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5339267331913169778&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The trick to remember here is that this only works for &quot;supported servers&quot; (e.g. I know that at least Tomcat and Glassfish are in that list) where the IDE would compile the new classes, and re-deploy them on the server. Jetty is not one of these supported servers, and in order for the Compile-on-save goodness to work, the IDE needs to know you ran the app so that it can activate COS. Now, although you probably don&#39;t want to run the app in Tomcat , go ahead and run the app, select to run it in Tomcat. Now that you ran the app in Tomcat, NetBeans activated COS for this app, and now if you make new changes to your Index.java, NetBeans copies out the compiled classes to target/classes, and Jetty picks up the changes. After you run the app, you can just stop Tomcat (and the COS feature will continue working). &lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;This is pretty close to perfect. Trouble is, if you have any page templates under src/main/resources, you&#39;re still out of luck, as the resources don&#39;t get copied out into target/classes after you do the initial jetty:run. But don&#39;t despair, there is just one more step that will get us there. &lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;br /&gt; Add the following to your pom.xml&lt;br /&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;&amp;lt;outputDirectory&amp;gt;${project.build.directory}/${project.build.finalName}/WEB-INF/classes&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;Now, this is a bit of a hack. Basically, we&#39;re telling maven to use the target/[app-name]/WEB-INF/classes to do the initial and any subsequent builds, which is where both the classes from src/main (and whereever else) and src/main/resources end up. The trick here is that this is the same directory that &quot;mvn package&quot; uses, and it is also the same directory that NetBeans uses for the Compile-on-save functionality. Basically, when you make changes to your page template sin src/main/resources (and after you&#39;ve run your app in Tomcat once), NetBeans continues to compile the classes and copy the modified resources from src/main/resources and drop them into the target/[app-name]/WEB-INF/classes. &lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;Considering that this is indeed a a hack, &lt;a href=&quot;http://www.netbeans.org/issues/show_bug.cgi?id=152974&quot;&gt;I filed a patch&lt;/a&gt; for NetBeans to properly support this T5 setup in Maven project. However, what got into 6.7 is only the fix to properly copy resources into target/[app-name]/WEB-INF/classes (and not in target/classes). The develoeper on the issue has some other ideas on how this should go, hopefully the full fix will go into the NetBeans version after 6.7. In the meantime, either use this little hack, or I&#39;ll probably try to repackage my fix as a standalone plugin to support this out of the box.&lt;br /&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/9175747913089605708/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2009/05/rad-w-tapestry-5-netbeans-67-maven-and.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/9175747913089605708'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/9175747913089605708'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2009/05/rad-w-tapestry-5-netbeans-67-maven-and.html' title='RAD w/ Tapestry 5, NetBeans 6.7, Maven, and Jetty : Really !!!'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtdKdcdcnQBrJ03nI0TXlp1EXv60Ycw8JYnYYug1JZSXtv0W246Clb_ckVThC4KmsTBgmeWQ-Ng8rvxkgyBSHnU3ExzRgZxeluWB9aZheEx5T4pyGgCnXhlLIjFcZLuvY_kFl1vQ/s72-c/maven_custom_goal1.png" height="72" width="72"/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-5673112230332670302</id><published>2009-05-06T00:45:00.016-04:00</published><updated>2009-05-13T00:34:30.936-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="grails"/><category scheme="http://www.blogger.com/atom/ns#" term="legacy rails"/><category scheme="http://www.blogger.com/atom/ns#" term="netbeans"/><category scheme="http://www.blogger.com/atom/ns#" term="rails"/><title type='text'>Converting legacy Rails apps to Grails : The Views</title><content type='html'>Alrighty, we&#39;re getting close to the finish line here. So far, we&#39;ve covered &lt;a href=&quot;http://www.troymaxventures.com/2008/10/converting-legacy-rails-apps-to-grails.html&quot;&gt;General Project Setup&lt;/a&gt;, &lt;a href=&quot;http://www.troymaxventures.com/2008/10/converting-legacy-rails-apps-to-grails_31.html&quot;&gt;Migrating the Domain&lt;/a&gt;, &lt;a href=&quot;http://www.troymaxventures.com/2009/05/converting-legacy-rails-apps-to-grails.html&quot;&gt;Migrating the Controllers&lt;/a&gt;, and now we&#39;ll talk about migrating the views. After that, if I have a little bit of life left in me, I&#39;ll briefly speak about replacing the existing plugins in the Rails app w/ equivalent Grails plugin, and that should be the end of this series. &lt;br /&gt;&lt;br /&gt;Now, on to the content. &lt;br /&gt;&lt;ol&gt;&lt;br /&gt;    &lt;li&gt;&lt;a href=&quot;#generalSetup&quot;&gt;General Setup&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;&lt;a href=&quot;#layouts&quot;&gt;Layouts&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;&lt;a href=&quot;#pageByPageMigration&quot;&gt;Page-by-page migration&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;&lt;a href=&quot;#tags&quot;&gt;Tags&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;    &lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;    &lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;&lt;a href=&quot;#&quot; name=&quot;generalSetup&quot;&gt;1. General Setup&lt;/a&gt;&lt;/span&gt;&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/AVvXsEieNU0nBQ2JkWKRr0lsrPZlmKZYqf1tHSdklV-GhadaeU8mHTHljDNUFzRTvHOik9ayAt5ZEDCmLRaDgB5mgTBR0JofUETUfvZAp7g9aKsx3H6djQ6M7RhYDVhMWPJ84NHdxC04yQ/s1600-h/rails_views_structure.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 240px; height: 291px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieNU0nBQ2JkWKRr0lsrPZlmKZYqf1tHSdklV-GhadaeU8mHTHljDNUFzRTvHOik9ayAt5ZEDCmLRaDgB5mgTBR0JofUETUfvZAp7g9aKsx3H6djQ6M7RhYDVhMWPJ84NHdxC04yQ/s320/rails_views_structure.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5332567692253458098&quot; /&gt;&lt;/a&gt;&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/AVvXsEhQ25gTpXXAHYTbZ07UgJqbdviq4mErsq-N9rVjptk_CZ9qs9W_lePZAyVeWt9b-GB8ox1WjvL-3tlEyJ35k4gS8_aeuUGDHryp826i18VGtvttwyO9qPgQzA-s4cn24LRNIXVCKQ/s1600-h/grails_views_structure.png&quot;&gt;&lt;img style=&quot;float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 221px; height: 320px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ25gTpXXAHYTbZ07UgJqbdviq4mErsq-N9rVjptk_CZ9qs9W_lePZAyVeWt9b-GB8ox1WjvL-3tlEyJ35k4gS8_aeuUGDHryp826i18VGtvttwyO9qPgQzA-s4cn24LRNIXVCKQ/s320/grails_views_structure.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5332567846874907314&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As you&#39;re probably used to it by now, the Grails and the Rails app have very similar approaches to storing views and templates. As you can see on the screenshots, the Rails views are in the &quot;Views&quot; project node, whereas in the Grails project, they&#39;re located in the &quot;View and Layouts&quot; project node. Inside of this folder, the views are partitioned by controller, e.g. the views and templates for controller &quot;FooController&quot; in the Grails app, sit inside of the view/foo subfolder. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;&lt;a href=&quot;#&quot; name=&quot;layouts&quot;&gt;2. Layouts&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Another interesting folder in both Rails and Grails is the Layouts folder (in Grails, Views and Layouts - layouts project folder). There, the projects store the differet project layouts. The general idea here is mostly the same: different parts of the app will have different layout needs. Migrating the layouts from Rails to Grails involves mostly tag-for-tag conversion of the rhtml to gsp. A couple of useful facts about that process. &lt;br /&gt;&lt;br /&gt;1. As mentioned in the &lt;a href=&quot;&quot;&gt;previous post&lt;/a&gt;, both frameworks have a reasonable set of defaults for the layout selection. I can&#39;t quite remember all the details about how Rails chooses its defaults, but the converted application mostly specified on a per-controller basis by specifying the element, e.g. : &lt;br /&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;class FooController &lt;br /&gt;    layout &quot;internal&quot;&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This Rails snippet will use the views/layouts/internal.rhtml layout. &lt;br /&gt;&lt;br /&gt;There is no direct equivalent for specifying the desired layout inside of a controller in Grails. Instead, a user can add a layout with the same name as the controller (e.g. layouts/foo.gsp for FooController). Although I don&#39;t recall this being used in the Rails app, Grails also provides the ability to specify a template to use for rendering a view by specifying a &amp;lt;meta name=&quot;layout&quot; content=&quot;landing&quot;&amp;gt;&amp;lt;/meta&amp;gt;. Rendering a view that specifies the layout in this way will use the view/layouts/internal.gsp layout. &lt;br /&gt;&lt;br /&gt;2. Converting the Rails layouts&lt;br /&gt;The Rails layouts that I worked with used the following statements in the &amp;lt;head&amp;gt; element: &lt;br /&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;head&amp;gt;&lt;br /&gt;&amp;lt;%= stylesheet_link_merged :base %&amp;gt;&lt;br /&gt;&amp;lt;%= stylesheet_link_merged :print, &#39;media&#39; =&amp;gt; &#39;print&#39; %&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;%= javascript_include_merged :base %&amp;gt;&lt;br /&gt;&amp;lt;%= javascript_include_merged :application %&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I can&#39;t quite say I know what all of the above statements do. I inspected the output the actual HTML output and replaced it with the following in my Grails template:&lt;br /&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;  &amp;lt;head&amp;gt;&lt;br /&gt; &amp;lt;link rel=&quot;shortcut icon&quot; href=&quot;${createLinkTo(dir:&#39;images&#39;,file:&#39;favicon.ico&#39;)}&quot; type=&quot;image/x-icon&quot; /&amp;gt;&lt;br /&gt; &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;${createLinkTo(dir:&#39;stylesheets/active_scaffold&#39;,file:&#39;stylesheet.css&#39;)}&quot; media=&quot;screen&quot; type=&quot;text/css&quot; /&amp;gt;&lt;br /&gt; &amp;lt;link href=&quot;${createLinkTo(dir:&#39;stylesheets&#39;,file:&#39;styles.css&#39;)}&quot; media=&quot;screen&quot; rel=&quot;Stylesheet&quot; type=&quot;text/css&quot; /&amp;gt;&lt;br /&gt; &amp;lt;link href=&quot;${createLinkTo(dir:&#39;stylesheets&#39;,file:&#39;print.css&#39;)}&quot; media=&quot;print&quot; rel=&quot;Stylesheet&quot; type=&quot;text/css&quot; /&amp;gt;&lt;br /&gt; &amp;lt;link href=&quot;${createLinkTo(dir:&#39;stylesheets/active_scaffold/default&#39;,file:&#39;stylesheet.css&#39;)}&quot; media=&quot;screen&quot; rel=&quot;Stylesheet&quot; type=&quot;text/css&quot; /&amp;gt;&lt;br /&gt; &amp;lt;!--[if IE]&amp;gt;&lt;br /&gt;  &amp;lt;link href=&quot;${createLinkTo(dir:&#39;stylesheets/active_scaffold/default&#39;,file:&#39;stylesheet-ie.css&#39;)}&quot; media=&quot;screen&quot; rel=&quot;Stylesheet&quot; type=&quot;text/css&quot; /&amp;gt;&lt;br /&gt; &amp;lt;![endif]--&amp;gt;&lt;br /&gt; &amp;lt;g:layoutHead /&amp;gt;&lt;br /&gt; &amp;lt;g:javascript library=&quot;application&quot; /&amp;gt;&lt;br /&gt; &amp;lt;g:javascript library=&quot;prototype&quot; /&amp;gt;&lt;br /&gt; &amp;lt;g:javascript library=&quot;scriptaculous&quot; /&amp;gt;&lt;br /&gt;  &amp;lt;/head&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;A couple of things to note here : &lt;br /&gt;* Using the ${createLinkTo()} tag inside of the stylesheet links : it is very convenient and makes the generation of the links pretty foolproof. &lt;br /&gt;* Using the &amp;lt;g:layoutHead /&amp;gt; statement : allows the inclusion of any elements from the &amp;lt;head&amp;gt; element of the &quot;client&quot; page (the page that is using the layout). &lt;br /&gt;&lt;br /&gt;Inside of the body of the template, the Rails template use the &amp;lt;%= yield%&amp;gt; statement to include the body of the client page. The equivalent statement in the Grails layout is the &amp;lt;g:layoutBody&amp;gt;&lt;br /&gt;&lt;br /&gt;Finally, Rails allows client views to contribute &quot;additional&quot; content into the final output. In other words, the template can define an &quot;area&quot; where the client template can contribute markup, in a way that the said markup shows up in the parts of the layout that are generally rendered by the template. For example, if the client pages need to contribute markup to the content of the &quot;sidebar&quot;, then, the layout would use something like the following:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;    &amp;lt;% if !@content_for_sidebar.nil? %&amp;gt;&lt;br /&gt;        &amp;lt;div id=&quot;right_sidebar_content_main&quot;&amp;gt;&lt;br /&gt;            &amp;lt;%= yield(:sidebar) %&amp;gt;&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;% end %&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The client template, contributes to the layout as follows: &lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;&amp;lt;% content_for :sidebar do%&amp;gt;&lt;br /&gt; &amp;lt;div&amp;gt; This content will show up under the &#39;right_sidear_content_main&#39; section in the final output &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In Grails, in order to implement the same feature, we have to resort to a less used and somewhat obscure feature of the underlying templating system that Grails users : Sitemesh. Here&#39;s the equivalent in the Grails project:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;    &amp;lt;g:if test=&quot;${pageProperty(name:&#39;page.sidebar&#39;)!=null &amp;&amp; pageProperty(name:&#39;page.sidebar&#39;)!=&#39;&#39;}&quot;&amp;gt;&lt;br /&gt;        &amp;lt;div id=&quot;right_sidebar_content_main&quot;&amp;gt;&lt;br /&gt;            &amp;lt;g:pageProperty name=&quot;page.sidebar&quot; /&amp;gt;&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;/g:if&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, in the &quot;client&quot; page, add content to the sidebar as follows: &lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;    &amp;lt;content tag=&quot;sidebar&quot;&amp;gt;&lt;br /&gt;          &amp;lt;g:render template=&quot;course_notes_side_bar&quot; /&amp;gt;&lt;br /&gt;    &amp;lt;/content&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There is some additional info on using this feature in the Grails docs&#39; for &lt;a href=&quot;http://grails.org/doc/1.1/ref/Tags/pageProperty.html&quot;&gt;pageProperty&lt;/a&gt; tag, but more so in the &lt;a href=&quot;http://www.opensymphony.com/sitemesh/index.html&quot;&gt;Sitemesh user docs&lt;/a&gt; and just &lt;a href=&quot;http://weblogs.java.net/blog/zarar/archive/2006/01/passing_arbitra.html&quot;&gt;random blogs&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;&lt;a href=&quot;#&quot; name=&quot;pageByPageMigration&quot;&gt;3. Page-by-page migration&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;There is in most cases a 1-1 relationship between the views in both frameworks. Converting from the Rails views to the Grails views was mostly mechanical : see the Rails tag, find the equivalent Grails tag, and then figure out how to map the Rails tag attributes to the Grails tag attributes. In most cases, the two are similar enough and the conversion is fairly easy. At other times, Rails did provide some more features not present in Grails and migrating the pages did require some level of thought and effort. Even in that case, even for the tags that don&#39;t have a Grails equivalent, after the first couple of tags the conversion is always the same. Here&#39;s an example of an easy conversion: &lt;br /&gt;&lt;br /&gt;In Rails:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt; &amp;lt;%= link_to_remote &quot;Upload File&quot;,&lt;br /&gt;       {:url =&amp;gt; { :controller =&amp;gt; &#39;activity_items&#39;, :action =&amp;gt; &#39;show_upload_form&#39;, :activity_id =&amp;gt; @act_id},&lt;br /&gt;          :before =&amp;gt; &quot;Element.show(&#39;show_upload_form&#39;)&quot; ,&lt;br /&gt;          :success =&amp;gt; &quot;Element.hide(&#39;show_upload_form&#39;);&quot;,&lt;br /&gt;    :update =&amp;gt; { :success =&amp;gt; &quot;upload_form&quot;, :failure =&amp;gt; &quot;upload_form_errors&quot; }}, &lt;br /&gt;        {:class =&amp;gt; &quot;action&quot;, :title =&amp;gt;&quot;Add a new file.&quot;} %&amp;gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In Grails: &lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;&amp;lt;g:remoteLink controller=&#39;activityItems&#39; action=&#39;show_upload_form&#39;&lt;br /&gt;        params=&quot;[act_id:act_id]&quot;&lt;br /&gt;        before=&quot;Element.show(&#39;show_upload_form&#39;)&quot;&lt;br /&gt;        onSuccess=&quot;Element.hide(&#39;show_upload_form&#39;)&quot;&lt;br /&gt;        update=&quot;[success:&#39;upload_form&#39;, failure:&#39;upload_form_errors&#39;]&quot;&lt;br /&gt;        class=&quot;action&quot; title=&quot;Add a new file&quot;&amp;gt; Upload File &amp;lt;/g:remoteLink&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Here&#39;s another one. &lt;br /&gt;&lt;br /&gt;Rails:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;&amp;lt;%= form_remote_tag :url =&amp;gt; {:action =&amp;gt; &#39;update&#39;,:controller =&amp;gt; &quot;activities&quot;, :id =&amp;gt; @activity.id},&lt;br /&gt;:before =&amp;gt; &quot;Element.show(&#39;form-indicator-activities&#39;)&quot; ,&lt;br /&gt;:success =&amp;gt; &quot;Element.hide(&#39;form-indicator-activities&#39;)&quot; %&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Grails:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;&amp;lt;g:formRemote url=&quot;[action:&#39;update&#39;, controller:&#39;activities&#39;, id:activity.id]&quot; name=&quot;editActivityForm&quot;&lt;br /&gt;    before=&quot;Element.show(&#39;form-indicator-activities&#39;)&quot;&lt;br /&gt;    onSuccess=&quot;Element.hide(&#39;form-indicator-activities&#39;)&quot;&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I found that because the Rails app assumed to be deployed at the root of the context (e.g. http://localhost:3000/), whereas the Grails app always assumes that it will be deployed to a non-root context path (e.g. /foo), in many cases I found myself replacing static image references with links created with ${createLinkTo()}&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;FROM: &lt;br /&gt;&amp;lt;img src=&quot;/images/indicator.gif&quot; id=&#39;addurl&#39; style=&#39;display:none;&#39; /&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;TO:&lt;br /&gt;&amp;lt;img src=&quot;${createLinkTo(dir:&quot;images&quot;,file:&quot;indicator.gif&quot;)}&quot; id=&#39;addurl&#39; style=&#39;display:none;&#39; /&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Here&#39;s an example of a Rails tag that didn&#39;t have a direct equivalent in Grails. &lt;br /&gt;&lt;br /&gt;Rails:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;&amp;lt;%= text_field_with_auto_complete(:course, :title, {:class=&amp;gt;&quot;SearchTextBox&quot;, :value =&amp;gt; &quot; type here, then hit enter&quot;, :onclick=&amp;gt;&quot;this.value=(this.value == &#39; type here, then hit enter&#39;)?&#39;&#39;:this.value;&quot;, :onblur =&amp;gt; &quot;this.value=(this.value == &#39;&#39;)?&#39; type here, then hit enter&#39;:this.value;&quot;}, completion_options = {})  %&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In Grails, I created a template that contained the same html + javascript that the Rails tag produced:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;&amp;lt;%-- The content below was migrated from the Rails app, could be improved if using a plugin providing a cleaner autocompletion setup --%&amp;gt;&lt;br /&gt;Search&lt;br /&gt;&lt;br /&gt;&amp;lt;style type=&quot;text/css&quot;&amp;gt;&lt;br /&gt;  div.auto_complete {&lt;br /&gt; width: 350px;&lt;br /&gt; background: #fff;&lt;br /&gt;  }&lt;br /&gt;  div.auto_complete ul {&lt;br /&gt; border:1px solid #888;                         &lt;br /&gt; margin:0;&lt;br /&gt; padding:0;&lt;br /&gt; width:100%;&lt;br /&gt; list-style-type:none;&lt;br /&gt;  }&lt;br /&gt;  div.auto_complete ul li {&lt;br /&gt; margin:0;&lt;br /&gt; padding:3px;&lt;br /&gt;  }&lt;br /&gt;  div.auto_complete ul li.selected {&lt;br /&gt; background-color: #ffb;&lt;br /&gt;  }&lt;br /&gt;  div.auto_complete ul strong.highlight {&lt;br /&gt; color: #800;&lt;br /&gt; margin:0;&lt;br /&gt; padding:0;&lt;br /&gt;  }&lt;br /&gt;&amp;lt;/style&amp;gt;&lt;br /&gt;&amp;lt;input autocomplete=&quot;off&quot; class=&quot;SearchTextBox&quot; id=&quot;course_title&quot; name=&quot;course.title&quot; onblur=&quot;this.value=(this.value == &#39;&#39;)?&#39; type here, then hit enter&#39;:this.value;&quot; onclick=&quot;this.value=(this.value == &#39; type here, then hit enter&#39;)?&#39;&#39;:this.value;&quot; size=&quot;30&quot; value=&quot; type here, then hit enter&quot; type=&quot;text&quot;&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div style=&quot;position: absolute; left: 1275px; top: 161px; width: 230px; display: none;&quot; class=&quot;auto_complete&quot; id=&quot;course_title_auto_complete&quot;&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;&lt;br /&gt;//&amp;lt;![CDATA[&lt;br /&gt;var course_title_auto_completer = new Ajax.Autocompleter(&#39;course_title&#39;, &#39;course_title_auto_complete&#39;, &quot;${createLink(controller:&#39;courses&#39;, action:&#39;auto_complete_for_course_title&#39;)}&quot;, {})&lt;br /&gt;//]]&amp;gt;&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Finally, Rails definitely has some more advanced scaffolding features that Grails didn&#39;t support out of the box or did not have a direct equivalent. Similarly to the example above, I just used the html that the scaffold generated, stuffed that into a separate template and used that. &lt;br /&gt;&lt;br /&gt;Rails:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;&amp;lt;%= render :active_scaffold =&amp;gt; &quot;notes&quot;, :constraints =&amp;gt; {:user_id =&amp;gt; current_user.id, :course_id =&amp;gt; @course.id} %&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In Grails, this became a standalone template (_notes_scaffold.gsp), which initially contained the static HTML generated by Rails, which I then rigged to support dynamically generate the needed markup (e.g. loop, etc). In the end, the call to the scaffold code above, becomes the following:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&lt;br /&gt;   &amp;lt;g:render template=&quot;/notes/notes_scaffold&quot; model=&quot;[notes:UserNote.findAllByUserAndCourse(user?:current_user(),course)]&quot; /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;&lt;a href=&quot;#&quot; name=&quot;tags&quot;&gt;4. Helpers and Tags&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;In a couple of instances, the Rails app depended on the &quot;helpers&quot; - seemingly a collection o methods that are available to be executed either from the view or contoller. I ended up encapsulating some of these common operations into taglibs, so that the usage of the said tags in grails is as follows: &lt;br /&gt;&lt;br /&gt;Rails (a sample Helper located in the Helpers project node):&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/AVvXsEjMUuF_NyGydn_kchw-2CnxsLt7DkbxgXJFnMJ5DjSOc0N2MA5FLX5pkNJew0m4FWWNxgLtcii5ocsls8D4j24GdPpDYRsVUtWyAEIaEr9OUuOa7QomAxVESaR7HY3aHpZPkgOR4Q/s1600-h/rails_helpers.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 237px; height: 204px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMUuF_NyGydn_kchw-2CnxsLt7DkbxgXJFnMJ5DjSOc0N2MA5FLX5pkNJew0m4FWWNxgLtcii5ocsls8D4j24GdPpDYRsVUtWyAEIaEr9OUuOa7QomAxVESaR7HY3aHpZPkgOR4Q/s320/rails_helpers.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5332604973157368386&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;require &#39;bluecloth&#39;&lt;br /&gt;module ApplicationHelper&lt;br /&gt; include TagsHelper&lt;br /&gt;    def link_to_button(label)&lt;br /&gt;     &quot;&amp;lt;table cellpadding=0 cellspacing=0 class=button_action_link&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td align=right style=\&quot;background: url(&#39;/images/left_button_curve.gif&#39;) no-repeat; width: 8px; height: 23px;\&quot;&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td nowarp=\&quot;nowrap\&quot; style=\&quot;background: url(&#39;/images/center_button_bg.gif&#39;) repeat;\&quot;&amp;gt; #{label} &amp;lt;/td&amp;gt;&amp;lt;td style=\&quot;background: url(&#39;/images/right_button_curve.gif&#39;) no-repeat; width: 29px; height: 23px;\&quot;&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;&quot;&lt;br /&gt;   end      &lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&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/AVvXsEgkE1cpkidK87KgJ1QWTYJ5zzu1IGO1bz9uig9BOqlAEPJbUmqr6Vg50X1Izqm4lDPBZ-QDYtp4sMNUOH4hGwwsICdVxiEECMBi16Fyaw_dq8rQYrehNN8FYXkyP4LKb9Crslw4Dw/s1600-h/grails_taglib.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 241px; height: 212px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkE1cpkidK87KgJ1QWTYJ5zzu1IGO1bz9uig9BOqlAEPJbUmqr6Vg50X1Izqm4lDPBZ-QDYtp4sMNUOH4hGwwsICdVxiEECMBi16Fyaw_dq8rQYrehNN8FYXkyP4LKb9Crslw4Dw/s320/grails_taglib.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5332605194522575074&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;In Grails, it becomes the following:&lt;br /&gt;class CosTagLib {&lt;br /&gt;&lt;br /&gt;    def linkToButton = { attrs -&gt;&lt;br /&gt;        out &amp;lt;&amp;lt; &quot;&amp;lt;table cellpadding=0 cellspacing=0 class=button_action_link&gt;&amp;lt;tr&gt;&amp;lt;td align=right style=\&quot;background: url(&#39;${createLinkTo(dir:&quot;images&quot;,file:&quot;left_button_curve.gif&quot;)}&#39;) no-repeat; width: 8px; height: 23px;\&quot;&gt;&amp;lt;/td&gt;&amp;lt;td nowarp=\&quot;nowrap\&quot; style=\&quot;background: url(&#39;${createLinkTo(dir:&quot;images&quot;,file:&quot;center_button_bg.gif&quot;)}&#39;) repeat;\&quot;&gt; ${attrs.label} &amp;lt;/td&gt;&amp;lt;td style=\&quot;background: url(&#39;${createLinkTo(dir:&quot;images&quot;,file:&quot;right_button_curve.gif&quot;)}&#39;) no-repeat; width: 29px; height: 23px;\&quot;&gt;&amp;lt;/td&gt;&amp;lt;/tr&gt;&amp;lt;/table&gt;&quot;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;An alternative to adding common helper functionality in tag libraries is to add the same methods as public methods on the superclass. For example, the parent controller contains the following closure:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;    def current_user = {&lt;br /&gt;  if (this.currentUser==null &amp;&amp; session.user) {&lt;br /&gt;   this.currentUser = User.get(session.user.id)&lt;br /&gt;  }&lt;br /&gt;        println &quot;Returning currentUser ${currentUser} &quot;&lt;br /&gt;  return currentUser;&lt;br /&gt; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then, in GSPs, one can use the closure as follows:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;   &lt;g:render template=&quot;/notes/notes_scaffold&quot; model=&quot;[notes:UserNote.findAllByUserAndCourse(user?:current_user(),course)]&quot; /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;&lt;a href=&quot;#&quot; name=&quot;conclusion&quot;&gt;5. Conclusion&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It&#39;s interesting that the migration of views/templates is probably the least complicated part of migrating a Rails app to Grails. Yet, at the same time, together w/ migrating the controllers it was possibly the most time consuming task. Understandably, these artifacts represent ARE the web application. While there probably isn&#39;t a good way to automatically convert the controller code, the view code is much more amenable to such an automated conversion, tag-for-tag. &lt;br /&gt;&lt;br /&gt;For a lot of these repetitive tasks of converting the app UI tag by tag ( I didn&#39;t spend the time to create an auto-converter), I ended up creating a couple of NetBeans live templates that give me parameter and code completion of attributes, jumping between different params, etc.</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/5673112230332670302/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2009/05/converting-legacy-rails-apps-to-grails_05.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/5673112230332670302'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/5673112230332670302'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2009/05/converting-legacy-rails-apps-to-grails_05.html' title='Converting legacy Rails apps to Grails : The Views'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieNU0nBQ2JkWKRr0lsrPZlmKZYqf1tHSdklV-GhadaeU8mHTHljDNUFzRTvHOik9ayAt5ZEDCmLRaDgB5mgTBR0JofUETUfvZAp7g9aKsx3H6djQ6M7RhYDVhMWPJ84NHdxC04yQ/s72-c/rails_views_structure.png" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-6050026609053975937</id><published>2009-05-05T17:37:00.013-04:00</published><updated>2009-05-13T00:34:14.549-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="grails"/><category scheme="http://www.blogger.com/atom/ns#" term="groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="legacy rails"/><category scheme="http://www.blogger.com/atom/ns#" term="netbeans"/><category scheme="http://www.blogger.com/atom/ns#" term="rails"/><title type='text'>Converting legacy Rails apps to Grails : The Controllers</title><content type='html'>So, you&#39;ve already looked at the previous blog posts on &lt;a href=&#39;http://www.troymaxventures.com/2008/10/converting-legacy-rails-apps-to-grails.html&#39;&gt;Setting up the Project&lt;/a&gt; and &lt;a href=&quot;http://www.troymaxventures.com/2008/10/converting-legacy-rails-apps-to-grails_31.html&quot;&gt;Migrating the Domain Objects&lt;/a&gt;. The whole world must be wondering &quot;What happened to this blog post series, did people just stop migrating from Rails to Grails?&quot;. Well, I&#39;ve been in &lt;a href=&quot;http://tapestry.apache.org/tapestry5&quot;&gt;Tapestry&lt;/a&gt; land for the last 6-7 months and haven&#39;t had much free time to finish my blogging endeavor to finish my series of articles. But, what do you know : all of a sudden the topic of migrating legacy Rails apps to Grails came back to the fore for me (work related, don&#39;t ask, it&#39;s top secret), and here I am. In a valiant effort, I will try to finish off the topic in one fell swoop (hopefully today) and bang out a couple of different articles that document in details the ups and downs of such a migration. &lt;br /&gt;&lt;br /&gt;Because this article is on the long side, here is the Table of Contents:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;#overviewOfControllers&quot;&gt;Overview of Controllers&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;#generalLanguageIssues&quot;&gt;General language related issues.&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;#inputProcessing&quot;&gt;Input Processing&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;#inputValidationAndErrors&quot;&gt;Input Validation and Error Reporting&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;#renderingResponses&quot;&gt;Rendering Responses&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;#advancedAjaxiness&quot;&gt;Advanced Ajaxiness : Dynamic Javascript&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;&lt;a href=&quot;#&quot; name=&quot;overviewOfControllers&quot;&gt;1. Overview of controllers&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikvRl3Vna3UJC-f18wwiXw3lFJwwwnUisDITkWBl5b7LEB06JJofzuBXMnNUKC0G7MnvLkrfDCrOAy8oQsuhQzzT-QB_iHv7360IfRVGePkGi3ktKUs0PGz0U1YrP1WaGS8iHuWw/s1600-h/rails_controllers.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 237px; height: 142px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikvRl3Vna3UJC-f18wwiXw3lFJwwwnUisDITkWBl5b7LEB06JJofzuBXMnNUKC0G7MnvLkrfDCrOAy8oQsuhQzzT-QB_iHv7360IfRVGePkGi3ktKUs0PGz0U1YrP1WaGS8iHuWw/s320/rails_controllers.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5332540880671369394&quot; /&gt;&lt;/a&gt;&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/AVvXsEi2h5Ls3bFuD6nXuypcePoGmNUJQHkXnw3zKRkOsTSS_ZHkMEvrqYQfRMa5FLCyuSzXo9bKksgscy2wc2Dff-5Ja9u9der8xNADZCD0M6hEYL5VBW3x75NtF4gW2w2jEDkG1Z8Wlw/s1600-h/grails_controllers.png&quot;&gt;&lt;img style=&quot;float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 233px; height: 173px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2h5Ls3bFuD6nXuypcePoGmNUJQHkXnw3zKRkOsTSS_ZHkMEvrqYQfRMa5FLCyuSzXo9bKksgscy2wc2Dff-5Ja9u9der8xNADZCD0M6hEYL5VBW3x75NtF4gW2w2jEDkG1Z8Wlw/s320/grails_controllers.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5332541061275262882&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now, back to the meat and potatoes of this article : migrating the Rails controllers to Grails. It&#39;s no secret that Grails heavily borrowed ideas from Rails (and NO, Grails is not Groovy on Rails, there&#39;s no mass transit involved at all, it&#39;s the good ole cup that everyone wants) and as can be seen from the screenshot of the project setup, both framework keep the controllers in the Controllers NetBeans project folder. Creating Grails controllers is easy: just right click on the Controllers project folder and select &quot;New Controller&quot;. NetBeans walks you through naming the Controller properly and creates the needed file and run the regular Grails &quot;create-controller&quot; task, which in creates a default view for the controller. &lt;br /&gt;&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/AVvXsEiPWCb5j5RWXasERg0LwVMAlIF4SS-q40d5H9wDdIwLRlUGHk4SPP_xKfE73G5DBtVunIDL3QiJ6sILixchYUl72SuRFkwCCozyM0lGxxqEokXcGMIrl9gM6YCf9xgli87dtihJYw/s1600-h/rails_controller_structure1.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 162px; height: 320px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPWCb5j5RWXasERg0LwVMAlIF4SS-q40d5H9wDdIwLRlUGHk4SPP_xKfE73G5DBtVunIDL3QiJ6sILixchYUl72SuRFkwCCozyM0lGxxqEokXcGMIrl9gM6YCf9xgli87dtihJYw/s320/rails_controller_structure1.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5332541300994691250&quot; /&gt;&lt;/a&gt;&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/AVvXsEhMzygX_Lfui1qM-d3lQZcFWlwQYegc02dAwzMMlc3l52LIUf6kddUGDb4S23orR7OkTW3fjKTf1vmEDWt-tF-o76sO5fwfsxYeZdIGl_Ic9WOgknN8vwxnmvJRpoWk2u1aHZryHQ/s1600-h/grails_controller_structure.png&quot;&gt;&lt;img style=&quot;float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 204px; height: 320px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMzygX_Lfui1qM-d3lQZcFWlwQYegc02dAwzMMlc3l52LIUf6kddUGDb4S23orR7OkTW3fjKTf1vmEDWt-tF-o76sO5fwfsxYeZdIGl_Ic9WOgknN8vwxnmvJRpoWk2u1aHZryHQ/s320/grails_controller_structure.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5332541448115918546&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The structure of the controllers themselves is very similar as well : in both cases, there is a one-to-one relationship between the Rails and Grails controllers. Inside the controller, in both cases, there is a class containing a bunch of closures , methods, and private member variables. In both cases, the closures in the controller become a part of the &quot;public api&quot; exposed by the controller, as all closures can be called from the URL (e.g. http://localhost:8080/app/controllerName/closure -&gt; http://localhost:8080/app/account/login). Private methods are not accessible to be invoked from the URL. For &quot;old school&quot; Java developers who might not be intimately familiar w/ Rails or Grails, it is interesting to note that the controllers are thread safe : that is, they can contain instance variables that will not be clobbered if two concurrent requests are sent to the same controller. A new controller instance is created for each Http request. &lt;br /&gt;&lt;br /&gt;OK, let&#39;s see what&#39;s inside the controller. Here&#39;s an example Rails controller: &lt;br /&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;class ActivityItemsController &lt; ApplicationController    &lt;br /&gt;  def create&lt;br /&gt;    @activity_item = ActivityItem.new(params[:activity_item])    &lt;br /&gt;    if @activity_item.save      &lt;br /&gt;      // do whatever &lt;br /&gt;    else&lt;br /&gt;      // do whatever else&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Converting this same controller to Grails would look like this: &lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;class ActivityItemsController extends ApplicationController {&lt;br /&gt;    def activity_item;&lt;br /&gt;    &lt;br /&gt;    def create = {&lt;br /&gt;        this.activity_item = new ActivityItem(params.activity_item)&lt;br /&gt;        if (activity.save()) {&lt;br /&gt;            // do whatever&lt;br /&gt;        } else {&lt;br /&gt;            // do whatever else&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;&lt;a name=&quot;generalLanguageIssues&quot; href=&quot;#&quot;&gt;2. General language related issues.&lt;/a&gt;&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;A lot of things to talk about here. First of all, just looking at the code it looks almost the same. The first superficial difference is the naming convention for the classes : in Rails user underscore_separated_file_names, whereas Groovy uses CamelCase. One notable difference is that in Grails, you do need to declare the class members, whereas in Rails (due to Ruby heritage), the properties can directly be assigned to when needed (e.g. @activity_item = ....). While the Ruby approach does save one line of code to declare the property, while migrating the code I found it very helpful to see the declarations at the top of the Groovy class. When you don&#39;t declare the class members upfront, it seems that it&#39;s quite easy to create a whole bunch of properties in the Ruby class w/o realizing how many you&#39;ve created, which generally can lead to muddying the interface (mind you, the said properties are publicly accessible - e.g. from views, other closures, methods, etc).&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;&lt;a href=&quot;#&quot; name=&quot;inputProcessing&quot;&gt;3. Input processing&lt;/a&gt;&lt;/span&gt; &lt;br /&gt;The second thing to note is the existence of the &quot;params&quot; map in both cases. In both cases, one can both read from and write to the params map using the accepted syntax : map[:key] in Ruby and map.key or map[key] in Groovy. So, nothing particularly interesting here. A bunch of other default objects are available in the Grails controller (probably quite familiar to Java Devs) such as servletContext, session, request, params, flash. Dealing with all is mostly the same in both frameworks and should be familiar to anyone done anything on the web.&lt;br /&gt;&lt;br /&gt;When processing input, in a number of places, Rails uses the following shortcut/idiom to bulk update the values of many attributes of an object from request parameters at once:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;   class Foo &lt;br /&gt;   &lt;br /&gt;    def bar &lt;br /&gt;        @activity.update_attributes(params[:activity])&lt;br /&gt;    end&lt;br /&gt;   end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To cut the long story short, this take in the value of request parameters and binds them to values in the object (note, this has severe security implications but that&#39;s a different topic to discuss). Grails offers an equivalent statement with: &lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;   class Foo {&lt;br /&gt;        def activity&lt;br /&gt;        &lt;br /&gt;        def bar = {&lt;br /&gt;            bindData(activity,params.activity)&lt;br /&gt;        }&lt;br /&gt;   &lt;br /&gt;   }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If using the straight out &lt;a href=&quot;http://grails.org/doc/1.1/ref/Controllers/bindData.html&quot;&gt;bindData&lt;/a&gt; method from Grails it accomplishes the same thing, with the same security implications. Whenever I actually bumped into examples like this, I tried to address some of the security issues by using the &quot;more advanced&quot; bindData method in Grails, which allows specifying parameters to exclude and a prefix of a property to use for binding, e.g. if I only wanted to bind the customer.name and customer.phone attributes from the request and I definitely wanted to prevent the customer.id attribute being affected, I&#39;d use something like this:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;bindData(myCustomerObject,params,[&quot;id&quot;], &quot;customer&quot;)&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;&lt;a href=&quot;#&quot; name=&quot;inputValidationAndErrors&quot;&gt;4. Input Validation and Error Reporting&lt;/a&gt;&lt;/span&gt;  &lt;br /&gt;In both framework, a large part of validating the input that is written to the domain model is done by specifying constraints in the domain model itself (e.g. see &lt;a href=&quot;http://www.troymaxventures.com/2008/10/converting-legacy-rails-apps-to-grails_31.html&quot;&gt;the article&lt;/a&gt; about the Rails-&gt;Grails domain migration). Thus, in both frameworks, code like this is pretty common: &lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;if @activity.save&lt;br /&gt;    // do whatever on success&lt;br /&gt;else&lt;br /&gt;   flash[:error]= @activity.errors.full_messages.join(&quot;&lt;br/&gt;&quot;) &lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In Grails, the code looks very similar:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;if (activity.save()) {&lt;br /&gt;    // do whatever on success&lt;br /&gt;} else {&lt;br /&gt;    flash.error = activity.errors&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;One minor difference here is that (at least in this app), the Rails just concatenated the error messages as text and placed them in the &quot;error&quot; property in flash scope. In contrast, Grails assigns the actual &quot;errors&quot; object to the same flash property, then allowing the view to render these error objects as it wishes (e.g. using the &lt;a href=&quot;http://grails.org/doc/1.1/ref/Tags/renderErrors.html&quot;&gt;g:renderErrors&lt;/a&gt; tag), which would allow rendering an error for a particular property, etc.&lt;br /&gt;&lt;br /&gt;In both frameworks, validation of can happen in the controller itself, and errors can be added to the relevant error property (in the appropriate scope). &lt;br /&gt;&lt;br /&gt;One more advanced feature of Grails that I found very useful later on in the conversion are the Grails are the form beans that you can use to populate values from the request (thus shielding from the security issues referred to further up), validating the input in a domain-class style approach, and generating errors in a nice and easy manner. So, here&#39;s the form object:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;&lt;br /&gt;public class ChangePasswordForm {&lt;br /&gt; String oldPassword;&lt;br /&gt; String password;&lt;br /&gt; String passwordConfirmation;&lt;br /&gt;&lt;br /&gt; static constraints = {&lt;br /&gt;  oldPassword(nullable:false,blank:false)&lt;br /&gt;  password(nullable:false, blank:false, size:4..40)&lt;br /&gt;  passwordConfirmation(nullable:false, blank:false, size:4..40,&lt;br /&gt;   validator: { oldPw, chgPwdCmd -&gt;&lt;br /&gt;    if (oldPw!=chgPwdCmd.password) {&lt;br /&gt;     return &quot;notsame.message&quot;&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  )&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You&#39;ll note the declarative syntax familiar from domain object validation, it&#39;s a beauty !!!&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;&amp;lt;g:formRemote url=&quot;[action:&#39;change_password&#39;]&quot; name=&quot;ChangePasswordForm&quot;&lt;br /&gt;       before=&quot;Element.show(&#39;form-indicator-pwd&#39;)&quot;&lt;br /&gt;       onSuccess=&quot;Element.hide(&#39;form-indicator-pwd&#39;)&quot;&amp;gt;&lt;br /&gt;                   &lt;br /&gt;&amp;lt;/g:formRemote&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Add the following to your grails-app/i18n/messages.properties for custom error messages:&lt;br /&gt;&lt;pre name=&quot;code&quot;&gt;&lt;br /&gt;#Custom messages&lt;br /&gt;forms.ChangePasswordForm.passwordConfirmation.notsame.message=Password confirmation not the same as password&lt;br /&gt;wrong.password.message.forms.ChangePasswordForm.password=Old password is wrong, please enter again&lt;br /&gt;forms.ChangePasswordForm.password.blank.message={0} cannot be blank&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Finally, using the form in the controller when submitted:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;def change_password = { ChangePasswordForm changePasswordForm -&gt;   &lt;br /&gt;        if (!changePasswordForm.hasErrors()) {   &lt;br /&gt;            // accessing the values from the form&lt;br /&gt;            def pwdValue = changePasswordForm.password&lt;br /&gt;            // adding a custom error to the form for an error not enforced in constraints&lt;br /&gt;            if (whateverRandomReasonYouWantToRejectAField) {&lt;br /&gt;                changePasswordForm.errors.rejectValue(&quot;password&quot;,&quot;wrong.password.message&quot;)&lt;br /&gt;            }&lt;br /&gt;            // do whatever &lt;br /&gt;        } else {&lt;br /&gt;            // do whatever else&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Finally, it seemed like a pretty common idiom in the Rails app to use dynamic javascript (I will talk about that plugin later ) to render errors back to the client:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;      if (@activity.save) &lt;br /&gt;            // do whatever on success &lt;br /&gt;      else&lt;br /&gt;        flash[:error] = &quot;#{@activity.errors.full_messages.join(&#39;&lt;br/&gt;&#39;)}&quot;&lt;br /&gt;          render :update do |page|&lt;br /&gt;            page.replace_html &quot;errors_div&quot;, :partial =&gt; &quot;common/errors_flash&quot;,:layout=&gt;false&lt;br /&gt;            // do whatever else to the page&lt;br /&gt;          end&lt;br /&gt;      end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In essense, this takes the validation errors, renders them using the &quot;errors_flash&quot; template, and replaces the content of the &quot;errors_div&quot; in the page with the rendering result. This approach caused me a lot of grief initially, but after a little bit of work it turned into the following in my Grails app: &lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;    if (activity_item.save()) {&lt;br /&gt;        // do whatever on success &lt;br /&gt;    } else {&lt;br /&gt;        js_error(activity_item.errors)&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Where the js_error method in the controller superclass, looks something like this (using the dynamic javascript plugin that will be discussed later): &lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;def js_error = { errors -&gt;&lt;br /&gt;    flash.error = errors&lt;br /&gt;    log.debug &quot;Sending errors back to client: ${errors}&quot;&lt;br /&gt;    renderJavascript {&lt;br /&gt;        update &#39;errors_div&#39;, [text:g.render(template:&quot;/common/errors_flash&quot;)]&lt;br /&gt;        callFunction &quot;Element.show&quot; , &#39;errors_div&#39;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;&lt;a href=&quot;#&quot; name=&quot;renderingResponses&quot;&gt;5. Rendering Responses&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;Converting the response rendering from Rails to Grails was pretty straightforward, here&#39;s the Rails example:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;  def new&lt;br /&gt;    render :partial =&gt; &quot;new&quot;, :layout =&gt; false&lt;br /&gt;  end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In Grails, this becomes:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;    def _new = {&lt;br /&gt;        render(template:&quot;new&quot;)&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;One tricky thing to note here is that because &quot;new&quot; is a Groovy keyword, I could not use the same closure name, NBD. The &lt;a href=&quot;http://grails.org/doc/1.1/ref/Controllers/render.html&quot;&gt;render controller method&lt;/a&gt; is pretty much the best thing since sliced bread and can render a whole bunch of things like regular pages, templates, XML, or JSON. Grails uses a convention that partial pages (templates) are named starting w/ an underscore. Thus, when you do:&lt;br /&gt;&lt;pre name=&quot;code&quot; style=&quot;java&quot;&gt;&lt;br /&gt;   render(template:&quot;fooTemplate&quot;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Grails finds the _footemplate.gsp file and renders it (equivalent to the Rails render :partial =&gt; &quot;footemplate&quot; which renders _footemplate.rhtml). &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;One other common idiom in the Rails app was to issue redirects from the controller: &lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;redirect_to  :action =&gt; &#39;show&#39;, :controller =&gt; &#39;activities&#39;, :id=&gt; params[:act_id]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Grails supports this idiom pretty nicely with the &lt;a href=&quot;http://grails.org/doc/1.1/ref/Controllers/redirect.html&quot;&gt;redicect controller method&lt;/a&gt; with pretty much the same parameters:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;redirect(controller:&#39;activities&#39;, action:&#39;show&#39;, id:params.act_id)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This particular project was using both script-centered and content-centered AJAX, and not much data-centered AJAX, so I didn&#39;t get to use JSON or XML rendering much; however, I always found the automatic marshalling to JSON or XML pretty cool: &lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;// Automatic marshalling of XML and JSON&lt;br /&gt;import grails.converters.*&lt;br /&gt;…&lt;br /&gt;render Book.list(params) as JSON&lt;br /&gt;render Book.get(params.id) as XML&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Finally, one cool feature of Rails that I initially missed was the ability to specify the default layout per controller :&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;class ActivitiesController &lt; ApplicationController&lt;br /&gt;  layout &quot;internal&quot;&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The statement above sets the default layout for this controller in the controller itself. Grails supports specifying the layout either by convention (e.g. grails-app/views/layouts/CONTROLLER.gsp or grails-app/views/layouts/CONTROLLER/ACTION.gsp , effectively equivalent to specifying layout=&quot;internal&quot; in the controller)  or explicitly in the template by specifying a meta tag (&amp;lt;meta name=&quot;layout&quot; content=&quot;internal&quot;&amp;gt;&amp;lt/meta&amp;lt;) in the template. &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;&lt;a href=&quot;#&quot; name=&quot;advancedAjaxiness&quot;&gt;6. Advanced Ajaxiness : Dynamic Javascript&lt;/a&gt;&lt;/span&gt; &lt;br /&gt;I was planning to discuss Rails plugins in a separate article; however, there is one particular Grails plugin that was extremely useful to cover a portion of Rails that Grails doesn&#39;t cover out of the box. More specifically, I&#39;m talking about the Rails script centered AJAX w/ dynamic javascript, e.g. : &lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;render :update do |page|&lt;br /&gt;    page.replace_html &quot;errors_div&quot;, :partial =&gt; &quot;common/errors_flash&quot;,:layout=&gt;false&lt;br /&gt;    page.replace_html &quot;show_activities&quot;, :partial=&gt;&quot;show&quot;, :layout=&gt;false&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As explained before, this replaces the content of the &quot;errors_div&quot; in the page w/ the rendered &quot;errors_flash&quot; template (which basically renders the flash errors in a list or something like that), and then replaces the content of the &quot;show_activities&quot; div w/ the content of the partial template. &lt;br /&gt;&lt;br /&gt;My initial approach was to change the actual pages to process the response and update the right div w/ the returned content, but it was a big PITA, considering how much the Rails app used this. Finally, after some searching on the net, I found the &lt;a href=&quot;http://www.grails.org/Dynamic+Javascript+Plugin&quot;&gt;Dynamic Javascript&lt;/a&gt; plugin. It had most of the features I needed to implement this Rails idiom with something like this: &lt;br /&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;renderJavascript {&lt;br /&gt;    update &#39;errors_div&#39;, [text:g.render(template:&quot;/common/errors_flash&quot;)]&lt;br /&gt;    callFunction &quot;Element.show&quot; , &#39;errors_div&#39;&lt;br /&gt;    update &#39;show_activities&#39;, [text:g.render(template:&quot;show_activities&quot;)]&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, in places where I needed to update multiple elements on the page, I ended up using this style of code in the controller itself. However, the majority of the uses were the following:&lt;br /&gt;* Render an error div&lt;br /&gt;* Replace a content div w/ the content of a template&lt;br /&gt;* A combination of the two. &lt;br /&gt;&lt;br /&gt;As a result, I moved the following code into the parent class of my controllers: &lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;def js_error = { errors -&gt;&lt;br /&gt;    flash.error = errors&lt;br /&gt;    log.debug &quot;Sending errors back to client: ${errors}&quot;&lt;br /&gt;    renderJavascript {&lt;br /&gt;        update &#39;errors_div&#39;, [text:g.render(template:&quot;/common/errors_flash&quot;)]&lt;br /&gt;        callFunction &quot;Element.show&quot; , &#39;errors_div&#39;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;def js_render = { replaceDiv,replaceContent -&gt;&lt;br /&gt;    renderJavascript {&lt;br /&gt;        replace replaceDiv, [text:replaceContent]&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;def js_error_and_replace = { errors, replaceDiv,replaceContent -&gt;&lt;br /&gt;    flash.error = errors&lt;br /&gt;    println &quot;Found errors ${errors}&quot;&lt;br /&gt;    renderJavascript {&lt;br /&gt;        update &#39;errors_div&#39;, [text:g.render(template:&quot;/common/errors_flash&quot;)]&lt;br /&gt;        callFunction &quot;Element.show&quot; , &#39;errors_div&#39;&lt;br /&gt;        replace replaceDiv, replaceContent&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Which significantly simplified the code in the actual controllers, e.g. : &lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;if (this.user.authenticate(user.login, changePasswordForm.password)) {&lt;br /&gt;    if (this.user.save()) {&lt;br /&gt;        flash.notice = &quot;Password Changed&quot;&lt;br /&gt;        js_render(&quot;change_password&quot;, g.render(template:&quot;change_password_form&quot;))&lt;br /&gt;    } else {&lt;br /&gt;        flash.notice = &quot;Password could not be changed&quot;&lt;br /&gt;        js_error(user.errors)&lt;br /&gt;    }&lt;br /&gt;} else {&lt;br /&gt;    println &quot;Didn&#39;t authenticate w/ password&quot;   &lt;br /&gt;    js_error_and_replace(changePasswordForm.errors,&quot;change_password&quot;,[text:g.render(template:&quot;change_password_form&quot;)])&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As mentioned above (and as expected), Grails support redirects pretty nicely. However, Javascript redirects , though not necessarily tricky, still required an extra piece of code to the parent controller:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;def js_redirect = { redirectUrl -&gt;&lt;br /&gt;    def jsRedirect = &quot;document.location = \&#39;$redirectUrl\&#39;&quot;    &lt;br /&gt;    renderJavascript {&lt;br /&gt;        appendJavascript jsRedirect;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;At which point, the code in the controller is really straightforward:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;js_redirect(createLink(controller:&#39;activities&#39;, action:&#39;show&#39;,id:params.id))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;However, one of the use cases involved a form executing in a frame (an upload form that did some status updates in a frame), which depending on the content returned in the frame, needed to redirect the whole browser window whenever the upload was done. In Rails it was handled as such: &lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;responds_to_parent do&lt;br /&gt;    render :update do |page|&lt;br /&gt;      flash[:notice] = &quot;File Uploaded Sucessfully&quot;&lt;br /&gt;      page.redirect_to :action =&gt;&#39;show&#39;, :controller=&gt;&#39;activities&#39;, :id=&gt;params[:activity_id]&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The code in the parent controller to support this idiom is as follows:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;def parent_redirect = { redirectUrl -&gt;    &lt;br /&gt;    render (text : &quot;&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&quot;+&lt;br /&gt;        &quot;&amp;lt;script type=&#39;text/javascript&#39; charset=&#39;utf-8&#39;&amp;gt;&quot;+&lt;br /&gt;            &quot;var loc = document.location;&quot;+&lt;br /&gt;            &quot;with(window.parent) {&quot;+&lt;br /&gt;            &quot; setTimeout(function() { &quot;+&lt;br /&gt;                    &quot;window.eval(document.location = \&#39;$redirectUrl\&#39;); loc.replace(&#39;about:blank&#39;); &quot;+&lt;br /&gt;                &quot;}, 1)&quot;+&lt;br /&gt;            &quot;}&quot;+&lt;br /&gt;        &quot;&amp;lt;/script&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&quot;)&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;One final note on the Dynamic Javascript plugin : the plugin that is uploaded on the Grails wiki is version 0.1 . The plugin appears to not be maintained actively; however, the linked author&#39;s blog (http://blog.peelmeagrape.net/2007/10/9/dynamic-javascript-plugin-for-grails) has version 0.2 of the plugin. Still, when I was heavily using this plugin, I ran into some issues with it and had to patch it as follows (in plugins/dynamic-javascript-0.2/src/groovy/JavascriptBuilder.groovy , commented out code is the broken part):&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;    private String renderTemplate(Map options)&lt;br /&gt;    {&lt;br /&gt;//        StringWriter s = new StringWriter()&lt;br /&gt;//        GrailsWebRequest webRequest = (GrailsWebRequest)RequestContextHolder.currentRequestAttributes();&lt;br /&gt;//        HttpServletResponse response = webRequest.getCurrentResponse();&lt;br /&gt;//        def writer = (RoutablePrintWriter)webRequest.getOut()&lt;br /&gt;        return controller.g.render(options);&lt;br /&gt;//        writer.destination.out.toString();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I posted on the author&#39;s blog w/ the proposed change, and later on received a notification that he liked the change and that he would incorporate it into the plugin. However, it appears that the plugin on his page is still at version 0.2 and my comment has disappeared from the blog. Oh, well... &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight:bold;&quot;&gt;&lt;a href=&quot;#&quot; name=&quot;conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;It was quite a journey so far. None of the problems in migrating controllers are particularly difficult or mind bending; however, there are just a lot of different issues to deal with if you&#39;re starting from scratch. So, now that you have all this good info, &lt;span style=&quot;font-weight:bold;&quot;&gt;START MIGRATING THAT RAILS APP THAT YOU&#39;VE BEEN EYEING, WOULD YA !!???!!!&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/6050026609053975937/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2009/05/converting-legacy-rails-apps-to-grails.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/6050026609053975937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/6050026609053975937'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2009/05/converting-legacy-rails-apps-to-grails.html' title='Converting legacy Rails apps to Grails : The Controllers'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikvRl3Vna3UJC-f18wwiXw3lFJwwwnUisDITkWBl5b7LEB06JJofzuBXMnNUKC0G7MnvLkrfDCrOAy8oQsuhQzzT-QB_iHv7360IfRVGePkGi3ktKUs0PGz0U1YrP1WaGS8iHuWw/s72-c/rails_controllers.png" height="72" width="72"/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-6550335578525453645</id><published>2008-11-01T00:18:00.019-04:00</published><updated>2009-05-05T14:12:36.575-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="grails"/><category scheme="http://www.blogger.com/atom/ns#" term="groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="netbeans"/><category scheme="http://www.blogger.com/atom/ns#" term="rails"/><title type='text'>Converting legacy Rails apps to Grails : The Domain</title><content type='html'>&lt;span style=&quot;font-weight: bold;font-size:100%;&quot; &gt;A little story about legacies...&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-size:85%;&quot;&gt;(skip this if you&#39;re not interested in hearing a sobbing sentimental story and just want to get to the good stuff)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;First a short story about my usage of &quot;legacy&quot; in the title of the post. So, here it is : a few years ago, Rails explodes on the scene. Everybody around you who knows a thing or two about web apps start thinking and asking whether your next app should be in Rails instead of XXX [substitute your framework here]. Nevermind that you&#39;ll be writing an &quot;enterprise application&quot; that would most likely need to integrate with the rest of your infrastructure (Java, PHP, whatever), or that the said application might have some performance requirements (e.g. it actually needs to DO something, instead of just pushing a few form feelds from and to the database).&lt;br /&gt;&lt;br /&gt;Alright, so, Ruby is cool, Ruby is all the rage. You bring in that intern that seems to be a Rails wizard, he totally blinded you with how he put together an app w/ 3-4 forms in less than an hour. Nobody on your existing team can do that : they want to &quot;think about the problem&quot;, &quot;understand what needs to be done&quot;, put some thought into how to do it, and only then start writing the code. Not so w/ your superstar intern : he&#39;s banging out page after page, form after form, it&#39;s glitzy and it&#39;s Ajaxy, your heart is about to melt from the love towards your unexpected intern saviour.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Fast forward to a few years later. Your intern is gone, he&#39;s onto his next new and exciting gig. Your loyal developers have learned a few tricks from the now &quot;old and crusty&quot; Rails app, you got the next version of your Java web framework and your devs are doing quite better with giving you the &quot;quick forms&quot; when you need them. Your customers, initially raving about how quickly they got their app, are now increasingly annoyed that when they ask for what is seemingly simple feature (e.g. hook into this other database that&#39;s not mysql, talk to that &#39;other app&#39; that&#39;s been there for a while) and your estimates are way too high (&#39;cause you have to write all that stuff from scratch). To make things worse, your developers actually popped the hood on the Rails app, and it&#39;s a big happy bowl of spaghetti : the controllers have their hands in everything : poking around the database, spitting back dynamic javascript groping the glitzy UI in the most unbelievable places (and btw, your devs don&#39;t want to touch it with a ten foot pole). When your company scored that big customer, everybody was enamoured by the cha-ching of the cash register, but nobody thought that all those new users will want to use your intern&#39;s app (which btw, turned out to not know much about web apps in general, as Rails was the first thing he learned), and now both new and existing customers are not so happy that it takes longer and longer for the app to service them. On top of that, there are very few people who do understand all the magic that happens under the hood in the Rails app, and there are yet fewer people who know how to scale it to the level you need.&lt;br /&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://www.archives.gov.on.ca/english/exhibits/christmas03/pics/3772_locomotive_820.jpg&quot;&gt;&lt;img style=&quot;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 820px; height: 522px;&quot; src=&quot;http://www.archives.gov.on.ca/english/on-line-exhibits/christmas-03/pics/3772_locomotive_820.jpg&quot; border=&quot;0&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;That&#39;s the place where the phrase &quot;Legacy Rails&quot; really starts making sense. Sure, there are new releases that promise a little more glitz to your app, the framework is still being actively developed, and nearly everyone out there has heard of Rails by now. But now that you&#39;re in this situation, can you really put your job on the line that just this next release will have the promised silver bullet ? Or would it maybe be better to just move the game back into your home court, where you set the rules, your dev team knows the ins and outs of the technology like the back of their hand, it scales well, integrates with EVERYTHING you could imagine ? That&#39;s when you really want that little Rails locomotive to let off some steam and disappear into the distance just as quickly as it arrived.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Anyway, I digress :-) Back to what I was talking about : how do you migrate the app to Grails.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;font-size:100%;&quot; &gt;Now The Goodies&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Below is a sample Rails model class that we&#39;ll use to talk about the migration:&lt;br /&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;class Activity &lt; ActiveRecord::Base&lt;br /&gt;  has_many :activity_items&lt;br /&gt;  has_many :user_notes  &lt;br /&gt;  belongs_to :competency_group&lt;br /&gt;  belongs_to :course&lt;br /&gt;  &lt;br /&gt;  has_many :activity_item_assets&lt;br /&gt;   &lt;br /&gt;  belongs_to :created_by, :class_name =&gt; &quot;User&quot;, :foreign_key =&gt; &quot;created_by&quot;&lt;br /&gt;  belongs_to :updated_by, :class_name =&gt; &quot;User&quot;, :foreign_key =&gt; &quot;updated_by&quot;&lt;br /&gt;  &lt;br /&gt;  validates_presence_of :title, :instruction_text, :competency_group_id&lt;br /&gt;  validates_length_of :title, :maximum  =&gt; AdminType::COURSE_TITLE_LENGTH&lt;br /&gt;  validates_uniqueness_of :title, :scope =&gt; [:competency_group_id]&lt;br /&gt;  &lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And the equivalent Grails domain object:&lt;br /&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;&lt;br /&gt;class Activity {&lt;br /&gt;&lt;br /&gt;String title, instructionText&lt;br /&gt;Date createdAt, updatedAt&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;static hasMany = [activityItems : ActivityItem, userNotes : UserNote, activityItemAssets:ActivityItemAsset]&lt;br /&gt;static belongsTo = [competencyGroup:CompetencyGroup, course : Course, createdBy : User, updatedBy : User]&lt;br /&gt;&lt;br /&gt;static constraints = {&lt;br /&gt;   title(nullable:false,blank:false,size:1..AdminType.COURSE_TITLE_LENGTH,unique:[&#39;competencyGroup&#39;])&lt;br /&gt;   instructionText(nullable:false, blank:false)&lt;br /&gt;   createdAt(nullable:true)&lt;br /&gt;   updatedAt(nullable:true)&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;static mapping = {&lt;br /&gt;   table &#39;activities&#39;&lt;br /&gt;   createdBy column:&#39;created_by&#39;&lt;br /&gt;   updatedBy column:&#39;updated_by&#39;&lt;br /&gt;   version false&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;1. Location &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Both Rails and Grails have a specific place where you can keep your domain objects. In Rails, you keep it in the app/models directory (the Models directory in your NetBeans project), whereas in Grails it&#39;s in grails-app/domain directory (the Domain Classes folder in the NetBeans project).&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;2. Purpose &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In both cases, the purpose of the domain objects represent the most important concepts in your application. Additionally, they typically are &quot;persistence capable&quot; (e.g. you can persist an instance w/ a single call), and they provide for a fairly simple specification of relationships w/ other domain objects, as well as allow for specifying validation rules.&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-IuaL64vHzCGXBQ2J-cmO-Q_ojowdGb8eeOQeiA2WVVEAu1ZCsMmf_W5YpiIpYyaGiq63Vi_us6oa9NtNSJIEfjpNlsKP-W4eDXZxf03xcs3BaOa_VBp-JBxkWLj63zkJs6yocg/s1600-h/grails_new_domain_class.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 400px; height: 259px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-IuaL64vHzCGXBQ2J-cmO-Q_ojowdGb8eeOQeiA2WVVEAu1ZCsMmf_W5YpiIpYyaGiq63Vi_us6oa9NtNSJIEfjpNlsKP-W4eDXZxf03xcs3BaOa_VBp-JBxkWLj63zkJs6yocg/s400/grails_new_domain_class.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5263842699190454530&quot; /&gt;&lt;/a&gt; &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/AVvXsEh-sQoiCOZbdRh93T4g31XQSTiNacVpQKLMA8_TrMjBGpPYmBFMcU2z3LVHiWTRbRm90_bvNUm0sUaq_Drn-336CHHMMc8r_CJeQ9ZTF9GChnCNPHWUqd5mRBzUINWcSrVtO3ok9A/s1600-h/grails_new_domain_class_wizard.png&quot;&gt;&lt;img style=&quot;float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 220px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-sQoiCOZbdRh93T4g31XQSTiNacVpQKLMA8_TrMjBGpPYmBFMcU2z3LVHiWTRbRm90_bvNUm0sUaq_Drn-336CHHMMc8r_CJeQ9ZTF9GChnCNPHWUqd5mRBzUINWcSrVtO3ok9A/s320/grails_new_domain_class_wizard.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5263844072521687410&quot; /&gt;&lt;/a&gt;&lt;br /&gt;NetBeans provides fairly basic support for creating the domain objects : you get a little wizard that asks for the name of the domain object and it creates the Groovy class for you. One of the cool things about how NetBeans handles Grails is that it doesn&#39;t create any new metadata (e.g. there&#39;s no project directory created), and because the NetBeans project system is based on Ant, the NetBeans project simply delegates the creation of the domain class to the Grails Ant scripts. &lt;br /&gt;&lt;br /&gt;Note that when you&#39;re looking at the differences between the Grails and the Rails classes, you will notice that (by design), the Grails class is much more focused on the domain, whereas the Rails class is much closer to the database. Thus, for example, you will notice that in the last line of the Rails validation, it references the &quot;competency_group_id&quot;. I would imagine this is where my lack of knowledge of the Rails CoC (convention over configuration) bit me in the back, but in a number of places (that I&#39;ll mention), the Rails code is allowed to reference &quot;assumed&quot; column names (based on the CoC), which is generally confusing, and also seems to be very refactoring unfriendly (e.g. what if at some point in life, I needed to have a slightly different colum name - would I have to hunt down &lt;span style=&quot;font-weight: bold;&quot;&gt;all&lt;/span&gt; references to that column)???&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;3. Class properties &amp;amp; Relationships&lt;/span&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;&lt;br /&gt;class Activity {&lt;br /&gt;&lt;br /&gt;   String title, instructionText&lt;br /&gt;   Date createdAt, updatedAt&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;   static hasMany = [activityItems : ActivityItem, userNotes : UserNote, activityItemAssets:ActivityItemAsset]&lt;br /&gt;   static belongsTo = [competencyGroup:CompetencyGroup, course : Course, createdBy : User, updatedBy : User]&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Migrating the class properties &amp;amp; relationships is pretty straightforward. For each &quot;simple&quot; property in the Rails class, you can declare a corresponding one in the Groovy class. Declaring the equivalent relationships in the Grails class is also quite straightforward, as the relationship names are pretty much the same. Although there is nothing in Groovy that prevents you from using the Rails naming conventions for properties (e.g. user_name), the Groovy convention is very much like in Java - CamelCase.&lt;br /&gt;&lt;br /&gt;This is probably due to my Java background, but I found the ability to declare a class member variable anywhere in the Ruby class much less readable than the equivalent Groovy class. What I mean is that, in the example above, the &quot;:title&quot; property is not mentioned anywhere before the validation constraint. Thus, in order to figure out the properties, you need to examine not only the relationships but also all validation constraints. Although in the Groovy class, the declaration of the relationships similarly defines properties in the class, but at least in validation, mapping, etc. you definitely need to refer to properties that are declared somewhere. I guess this probably comes down to preference, but in my opinion, looking at the Grails class, I can see all the available properties at a glance.&lt;br /&gt;&lt;br /&gt;In a very similar manner, I find that even the relationship declarations are very much more readable in Grails. One glance, and I can recognize all relationship types (e.g. one to one, many-to-one, whatever) and the properties corresponding to those relationships. In contrast, although the same can be accomplished in the Rails declarations (e.g. if you specify all has_one mentions one after another), not all Rails model classes that I had to look at followed such a convention.&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;4. Validation &lt;/span&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;static constraints = {&lt;br /&gt;   title(nullable:false,blank:false,size:1..AdminType.COURSE_TITLE_LENGTH,unique:[&#39;competencyGroup&#39;])&lt;br /&gt;   instructionText(nullable:false, blank:false)&lt;br /&gt;   createdAt(nullable:true)&lt;br /&gt;   updatedAt(nullable:true)&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Once again, migrating the Rails validations is pretty straightforward, although not all Rails validations had a 1:1 translation in Grails. This is where &lt;a href=&quot;http://grails.org/doc/1.0.x/guide/single.html#7.%20Validation&quot;&gt;the validation section of the Grails manual &lt;/a&gt; came in very handy, expecially during the first steps in the conversion when I wasn&#39;t really sure how to convert from one constraint in Rails to its Grails counterpart.&lt;br /&gt;&lt;br /&gt;One thing to notice that is subtle but different between the Grails and Rails validations. In Grails, if a class property is not explicitly declared as &quot;nullable&quot;, it is by default required. On quite a few occasions during the conversion, after initially migrating the explicit Rails constraints, I found myself going back to the domain class in order to make some of the Grails domain class fields optional.&lt;br /&gt;&lt;br /&gt;One final point on validation is the  title uniqueness constraint. In Rails it looks like :&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;validates_uniqueness_of :title, :scope =&gt; [:competency_group_id]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And in Grails it is :&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;title(nullable:false,blank:false,size:1..AdminType.COURSE_TITLE_LENGTH,unique:[&#39;competencyGroup&#39;])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The thing to notice here is that Rails directly goes to the colum name, whereas Grails just uses the property name declared in the class.&lt;br /&gt;&lt;br /&gt;Once again, I find the Grails validation section much more readable, as all constraints are organized in one section, and they&#39;re organized around the concepts that a user cares about. Thus, when I&#39;m thinking about the validations that apply to a title, I can specify all constraints in the title constraints, compared to the Rails style, where the declarations are focused around the constraints (e.g. when you are thinking about a concept in the domain, do you think &quot;Hm, let me figure out which properties of this class might need a format constraint?&quot;, or do you think &quot;Hm, let me see, a title, what kind of constraints might it need, maybe a format constraint?&quot;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;5. Mapping into the database &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;&lt;br /&gt;static mapping = {&lt;br /&gt;   table &#39;activities&#39;&lt;br /&gt;   createdBy column:&#39;created_by&#39;&lt;br /&gt;   updatedBy column:&#39;updated_by&#39;&lt;br /&gt;   version false&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;As you see in the &quot;mapping&quot; section of the Grails class, there were a few attributes that needed to mapped explicitly. The reason I had to do this is that at least during the initial migration, it was preferable that the Rails and Grails app work off the same database schema, so that the two apps can be tested side-by-side on the same data.&lt;br /&gt;&lt;br /&gt;The mapping of the Grails app into the Rails database was pretty straightforward, as they follow very similar naming conventions for naming the database columns. First, Rails has the habit of converting the domain class names to plural for the table names, thus I had to add the mapping in the Grails app to point to the same tables. Additionally, the Rails class explicitly stated that the foreign keys that connect the user and activity are &quot;created_by&quot; and &quot;updated_by&quot;, whereas the default Grails naming convention for the foreign keys would have been &quot;created_by_id&quot; and &quot;updated_by_id&quot;, hence the additional mapping.&lt;br /&gt;&lt;br /&gt;You will additionally notice the explicit disabling of the &quot;version&quot; column in the Grails class. The issue here is that by default, Grails uses a &quot;version&quot; column in order to allow Hibernate to do optimistic locking in transactions. Note that removing the version column from the Grails  app has its penalties; however, at least in the initial implementation it was more important to have the same database schema, than to focus on performance. Although initially (before disabling the column) Grails very gracefully handled the addition of the new &quot;version&quot; column to the database, it became an issue when the column was added on a populated database, and the version would receive a null default value. Thus, had I decided to keep it for each table, I would have had to update the version column manually to contain 0 (so that Grails could increment as necessary, otherwise a NPE came up when Grails pulled a null from the database).&lt;br /&gt;&lt;br /&gt;It is recommended that such a version column is restored after the initial migration period in order to allow Hibernate to make use of its optimistic locking performance optimizations.&lt;br /&gt;&lt;br /&gt;6. Non persistent attributes&lt;br /&gt;By default, all attributes defined in the class are persisted in the database. Now, in the example of the User class below, the cleartext password really shouldn&#39;t be persisted. Rails gets around it by  declaring it as a virtual attribute:&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;ruby&quot;&gt;&lt;br /&gt;class User &lt; ActiveRecord::Base  &lt;br /&gt;  # Virtual attribute for the unencrypted password&lt;br /&gt;  attr_accessor :password&lt;br /&gt;  &lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The Grails domain class takes a slightly different approach, where the field is still declared, but is also mentioned in a special class attribute to indicate to Grails that the field shouldn&#39;t be persisted , and the &#39;password false&#39; in the mapping section to indicate that a column shouldn&#39;t be created in the database.&lt;br /&gt;&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;class User {&lt;br /&gt;    static mapping = {&lt;br /&gt;       table &#39;users&#39;&lt;br /&gt;       password false&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    static transients = [ &quot;password&quot;]&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;7. Persistence events&lt;br /&gt;&lt;pre name=&quot;code&quot; class=&quot;java&quot;&gt;&lt;br /&gt;class Foo {&lt;br /&gt;    def beforeInsert = {&lt;br /&gt;        makeActivationCode()&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As mentioned above, Rails supports the ability to automatically hook into the persistence events , in the case above, to execute the specified closure at a particular point in the persistence lifecycle. Grails doesn&#39;t support this out of the box, but it is extremely easy to accomplish the same functionality by installing the Grails &lt;a href=&quot;http://docs.codehaus.org/display/GRAILS/Hibernate+Events+Plugin&quot;&gt;Hibernate Events Plugin&lt;/a&gt;, and then specify a couple of specially named closures&lt;br /&gt;&lt;br /&gt;All in all, throughout the whole conversion I had the feeling that when the Grails folks sat down to figure out how to do things, they put a little bit of thought into how developers actually work with domain classes, what&#39;s readable, and what&#39;s not, whereas the Rails approach has a little bit more of a &quot;hacked up&quot; feel to it.</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/6550335578525453645/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2008/10/converting-legacy-rails-apps-to-grails_31.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/6550335578525453645'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/6550335578525453645'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2008/10/converting-legacy-rails-apps-to-grails_31.html' title='Converting legacy Rails apps to Grails : The Domain'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-IuaL64vHzCGXBQ2J-cmO-Q_ojowdGb8eeOQeiA2WVVEAu1ZCsMmf_W5YpiIpYyaGiq63Vi_us6oa9NtNSJIEfjpNlsKP-W4eDXZxf03xcs3BaOa_VBp-JBxkWLj63zkJs6yocg/s72-c/grails_new_domain_class.png" height="72" width="72"/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-6271821059087895391</id><published>2008-10-23T23:23:00.009-04:00</published><updated>2009-01-09T14:41:27.432-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="grails"/><category scheme="http://www.blogger.com/atom/ns#" term="groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="netbeans"/><category scheme="http://www.blogger.com/atom/ns#" term="rails"/><title type='text'>Converting legacy Rails apps to Grails (with NetBeans) : General Project Setup</title><content type='html'>It is hard to describe the pleasure of writing the title above, especially the &quot;legacy&quot; part :-) Although lately the jumping up and down of Ruby &amp; Rails fanboys has subsided a little, after &lt;a href=&quot;http://www.oreillynet.com/ruby/blog/2007/09/7_reasons_i_switched_back_to_p_1.html&quot;&gt;people started realizing&lt;/a&gt; that Rails is not going to kill anything (much less Java, hell if PHP people start going back to PHP that says A LOT!!!). So, a few years later I got a chance to chime in on the subject. &lt;br /&gt;&lt;br /&gt;Anyway, the specific reason for writing this post is that I had a chance to take an existing Rails app, and move it to Grails. Some of the reasons for even attempting is that as much effort and hype has gone into Ruby and Rails, the (C)Ruby is not even close in being able to integrate with everything else like Java can. When you throw into the mix that even JRuby is starting to be faster than (C)Ruby, and that Groovy kicks the pants out of JRuby, there&#39;s also a performance story to be told for Grails. Finally, when you throw in the existence of a clear migration path in performance sensitive Groovy/Grails components directly into java (e.g. moving Groovy controllers to Spring MVC controllers, or Groovy services to Java services), migrating from Rails to Grails for integration for performance purposes is just a no brainer. &lt;br /&gt;&lt;br /&gt;Anyway, enough of this high level stuff. I&#39;m sure that if some Rails fanboys get a hold of this article, I&#39;ll probably get flamed with claims how Ruby &amp; Rails can perform, and who knows what else, but I&#39;ll just leave the high level stuff at what I said above, and I&#39;ll focus on on comparing what I saw in Rails from the point of view of a  Grails (and long term Java) developer. Additionally, I will be using a daily NetBeans 6.5 build (very close to RC1) to illustrate the steps I take along the way. &lt;br /&gt;&lt;br /&gt;So, let&#39;s get started. &lt;br /&gt;&lt;br /&gt;1. First, getting started with a project. In both Grails and Rails it&#39;s very simple to get started with the facilities NetBeans provides. For both Grails and Rails , NetBeans 6.5 provides wizards for creating a new project. &lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguQivD-yCs222Mt_ZbZ_ZtCJ5xPuMVZxelzarPNIfftZjIpx6Nu9KDqMCPbF5UREg8KEg8mdwxAon0_WgsB7K_iWBpwfzkdooeHTqfEUhs0j-mKU6NWiy0OewTGSEekomMJi5CQg/s1600-h/new_grails_project_setup.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 320px; height: 134px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguQivD-yCs222Mt_ZbZ_ZtCJ5xPuMVZxelzarPNIfftZjIpx6Nu9KDqMCPbF5UREg8KEg8mdwxAon0_WgsB7K_iWBpwfzkdooeHTqfEUhs0j-mKU6NWiy0OewTGSEekomMJi5CQg/s320/new_grails_project_setup.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5260568022344504818&quot; /&gt;&lt;/a&gt;&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/AVvXsEgwnF3aMY5YnNUtaJ0e-QIj6rJQZku0ZtsqjIf1GTHWUvQmoW4XbQb5gCR79XMNOGu84B3AO_vumwp4mLL73SVF_iETeMJOxriFKOaPSpAvRFJ4WFTcDhyphenhyphenRk_YK17EGNkSjALCKXQ/s1600-h/new_rails_project_setup2.png&quot;&gt;&lt;img style=&quot;float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 320px; height: 132px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwnF3aMY5YnNUtaJ0e-QIj6rJQZku0ZtsqjIf1GTHWUvQmoW4XbQb5gCR79XMNOGu84B3AO_vumwp4mLL73SVF_iETeMJOxriFKOaPSpAvRFJ4WFTcDhyphenhyphenRk_YK17EGNkSjALCKXQ/s320/new_rails_project_setup2.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5260568199626900578&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now, a few comments on the contents of the screenshots. First of all, NetBeans 6.5 ships directly with Groovy &amp; Grails support for the &quot;Java&quot; packaged download (e.g. from http://download.netbeans.org/netbeans/6.5/rc/). In contrast, for the Ruby/Rails support, you have to either download the Ruby package from the same location, or after downloading the Java installer / package, you have to go to the plugin center and install the Ruby/Rails plugin (either way, quite easy). Secondly, when you look at the options for Ruby/Rails projects, you might think that there are more options w/ Ruby and Rails, but it is actually quite deceiving. The reason that there are no separate options to create a &quot;new Grails app w/ existing sources&quot; is that NetBeans totally rocks and can directly open an existing Grails project without having to add any additional project data. On the Groovy front, you can just add Groovy scripts and classes into any Java project, so the extra options in the project menu are just not needed. &lt;br /&gt;&lt;br /&gt;So, at first glance, very marginally and despite the outstanding NetBeans Ruby/Rails support, Grails scores the first point for me. &lt;br /&gt;&lt;br /&gt;2. Second, I had to figure out what the general setup of a Rails app. Here&#39;s what a typical project structure looks like in both Grails and Rails: &lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXi5fj1tK5dlWGxiJj-YvUEYMxP5q8uIZO0wdbuGa_SKWLiTjwULbs5ODapjG0fSD3ltgzIF55MHwxRkvRBV-YJsgWIfzPZvXtl4OvyP1NhyMQLwyKR-jT-mcWKFAe0fJBFKUl5Q/s1600-h/grails_project_setup.png&quot;&gt;&lt;img style=&quot;float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 285px; height: 312px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXi5fj1tK5dlWGxiJj-YvUEYMxP5q8uIZO0wdbuGa_SKWLiTjwULbs5ODapjG0fSD3ltgzIF55MHwxRkvRBV-YJsgWIfzPZvXtl4OvyP1NhyMQLwyKR-jT-mcWKFAe0fJBFKUl5Q/s320/grails_project_setup.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5260571041171657826&quot; /&gt;&lt;/a&gt;&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/AVvXsEiAP2a6MfL50xQ28z6XDekWALp563aGVyhKJ11i-Df04HwO9oL1R27uIgFMJPqW_ygl_HnW-XIFQza7oKcRaeOrd-wASUq7LEr-v06DirwDddmrvcPHr72ryb6b72-V88Ou7R42Yg/s1600-h/rails_project_setup1.png&quot;&gt;&lt;img style=&quot;float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 234px; height: 320px;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAP2a6MfL50xQ28z6XDekWALp563aGVyhKJ11i-Df04HwO9oL1R27uIgFMJPqW_ygl_HnW-XIFQza7oKcRaeOrd-wASUq7LEr-v06DirwDddmrvcPHr72ryb6b72-V88Ou7R42Yg/s320/rails_project_setup1.png&quot; border=&quot;0&quot; alt=&quot;&quot;id=&quot;BLOGGER_PHOTO_ID_5260571167514832562&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now, comparing the two project structures, it&#39;s very easy to get around the Rails app if you&#39;re familiar w/ a Grails project layout. A couple of things to note in the Rails project structure : &lt;br /&gt;* The root of the web app is in the &quot;Public&quot; directory in the Rails app, while in the Grails project it&#39;s in the &quot;Web Application&quot; project folder. &lt;br /&gt;* There is a &quot;helpers&quot; folder in the Rails app, which initially puzzled me. In most general terms, the helpers folder contains &quot;controller helpers&quot;. Roughly speaking, the functionality that existed in &quot;helpers&quot; in the Rails app eventually founds its way in taglibs in the Grails app. Although I&#39;m not 100% certain of the convention and usage of these helper methods in the Rails app, it seemed like the Rails active scaffold seemed to use some parts of what&#39;s in the helpers (however, i could be wrong). &lt;br /&gt;* There is the &quot;Migrations&quot; folder in the Rails project, which seems to be generally useful and not present in the Grails app. Now, I would imagine that such a database centric functionality might not be that relevant in a Grails app, as a Grails app really isn&#39;t as aware of the database (as it deals w/ the domain model and not database columns as the Rails app does). Still, it seems like because Grails apps end up very much data driven, some method of managing the schema modifications could be generally useful (although, I really don&#39;t have any specific suggestions of what such a tool might be). &lt;br /&gt;* (although you don&#39;t see this in the screenshots) If you had plugins installed into your Grails app, you would have a &quot;Plugins&quot; folder, which is roughly equivalent to the Rails &quot;Vendors&quot; foler. &lt;br /&gt;* Finally, the Grails app has a folder called &quot;Services&quot; for creating transactional services , for which the Rails app doesn&#39;t have an explicit counterpart. More on transaction handling later. &lt;br /&gt;&lt;br /&gt;All in all, in the rest of the project layout, they&#39;re very similar, and if one knows the one framework, it&#39;s pretty straightforward to grok the other one. &lt;br /&gt;&lt;br /&gt;Alright, this is the general setup stuff so far. I have a lot more to write about : plugins, ajax support, services, transactions, the whole nine yards. Just to give you a sneak preview, the bottom line is that migrating a Rails app to Grails works very nicely, although not without a few minor hurdles to jump over. More on that in the next post, stay tuned...</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/6271821059087895391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2008/10/converting-legacy-rails-apps-to-grails.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/6271821059087895391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/6271821059087895391'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2008/10/converting-legacy-rails-apps-to-grails.html' title='Converting legacy Rails apps to Grails (with NetBeans) : General Project Setup'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguQivD-yCs222Mt_ZbZ_ZtCJ5xPuMVZxelzarPNIfftZjIpx6Nu9KDqMCPbF5UREg8KEg8mdwxAon0_WgsB7K_iWBpwfzkdooeHTqfEUhs0j-mKU6NWiy0OewTGSEekomMJi5CQg/s72-c/new_grails_project_setup.png" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-4049835707454928147</id><published>2008-07-08T07:04:00.006-04:00</published><updated>2008-07-11T06:03:33.083-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="grails"/><category scheme="http://www.blogger.com/atom/ns#" term="groovy"/><category scheme="http://www.blogger.com/atom/ns#" term="testing"/><title type='text'>Switch your test to Groovy ? Maybe not (yet).</title><content type='html'>When I attended the No Fluff Just Stuff conference last year, all speakers were pushing Groovy as an excellent choice for everybody&#39;s unit testing needs. And it is true that Groovy does bring a number of cool features to the testing party : expressiveness, ability to test private methods, mocks are almost built in the language.. So it&#39;s cool, no question about it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;In order for a tool to be effective as a unit testing tool, two things need to be true of the tool:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt; The tool has to be expressive when reading and writing the tests. In other words, the way the test are written should clearly express the purpose of the test. &lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt; When tests fail, the failures should very clearly pinpoint the reason for the failure and should help the developer immediately know the cause of the failure&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now, how does Groovy measure up ?&lt;br /&gt;&lt;br /&gt;One of the big advantages of using an integrated framework like Grails (when I say integrated, I mean a framework that give you out of the box the whole stack : Ajax, web layer / controller, service layer, persistence) is that it is extremely easy to start writing tests. You just write &quot;grails create-test&quot; on the command line, and you already have the shell for the test. Then, when you want to run the test, you can easily run the test by just running &quot;grails test-app&quot;, and voilla, all your unit and integration tests are off and running.&lt;br /&gt;&lt;br /&gt;So, on the first criteria, Groovy really shines. Expressing the intent of the test with Groovy or verifying a particular test condition is way much better than doing the same in plain on Java. Often, reading a test written in Groovy is so much easier to understand what the test writer had in mind, it takes way less code to write and maintain. All in all, Groovy rocks here.&lt;br /&gt;&lt;br /&gt;Now, the second part, pinpointing the cause of the error, I can&#39;t say much beyond what the stacktrace below says. Here are a couple of questions on the stacktrace below:&lt;br /&gt;&lt;br /&gt;1. Can you make out where exactly the test fails ?&lt;br /&gt;2. If this was a stack trace out of an error, would you be able to make out where the error occured ?&lt;br /&gt;3. Can you figure out what classes are collaborating in your test ?&lt;br /&gt;&lt;br /&gt;I think the answer to all three questions is probably a &quot;no&quot;, or &quot;it&#39;s not easy&quot;. For me, this is quite a show stopper for moving all my tests to Groovy (which might have previously been in Java). One thing about plain Java is just that : it&#39;s plain and simple to follow along. You see a stacktrace, and you immediately know what, how, who went wrong. Now, I could certainly agree that if you take Java, weave in some aspects, or throw in some interceptors (as in the case of EJB), the stacktrace can easily resemble what&#39;s below.&lt;br /&gt;&lt;br /&gt;P.S. One final thing about &lt;a href=&quot;http://www.nofluffjuststuff.com/&quot;&gt;NFJS&lt;/a&gt; and the push to use Groovy everywhere : it really bugs me that all of smart people who spoke in favor of using Groovy as a testing tool, just kept quiet about things like this. It bugs me very much that when an alternative like this was suggested, I was just given the positive side of the story, and the negatives were not mentioned at all (and mind you, there were definitely questions like &quot;When would you not use Groovy for testing?). So, there seem to be two disappointing options here : either the speakers had not really used Groovy for testing and were pushing without having done any testing in Groovy themselves, or even worse, knew about these warts but intentionally kept shtumm on it&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Cannot cast object &#39;com.company.foobar.RandomClassToTest@194e3fe&#39; with class &#39;com.company.foobar.RandomClassToTest&#39; to class &#39;java.util.List&#39;&lt;br /&gt;&lt;br /&gt;org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object &#39;com.company.foobar.RandomClassToTest@194e3fe&#39; with class &#39;com.company.foobar.RandomClassToTest&#39; to class &#39;java.util.List&#39;&lt;br /&gt;at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.castToType(DefaultTypeTransformation.java:340)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.castToType(ScriptBytecodeAdapter.java:628)&lt;br /&gt;at config.foo.WorkflowConfigTests.transitionsWithStatus(WorkflowConfigTests.groovy:90)&lt;br /&gt;at config.foo.WorkflowConfigTests.this$5$transitionsWithStatus(WorkflowConfigTests.groovy)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:77)&lt;br /&gt;at config.foo.WorkflowConfigTests.validateSingleTransition(WorkflowConfigTests.groovy:64)&lt;br /&gt;at config.foo.WorkflowConfigTests.this$5$validateSingleTransition(WorkflowConfigTests.groovy)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:867)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:77)&lt;br /&gt;at config.foo.WorkflowConfigTests$_testExistingTransitionRules_closure1.doCall(WorkflowConfigTests.groovy:56)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.Closure.call(Closure.java:292)&lt;br /&gt;at groovy.lang.Closure.call(Closure.java:305)&lt;br /&gt;at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1041)&lt;br /&gt;at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:1018)&lt;br /&gt;at org.codehaus.groovy.runtime.metaclass.ReflectionMetaMethod.invoke(ReflectionMetaMethod.java:51)&lt;br /&gt;at org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod.invoke(NewInstanceMetaMethod.java:54)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokePojoMethod(InvokerHelper.java:765)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:753)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:167)&lt;br /&gt;at config.foo.WorkflowConfigTests.testExistingTransitionRules(WorkflowConfigTests.groovy:55)&lt;br /&gt;at org.codehaus.groovy.grails.support.GrailsTestSuite.runTest(GrailsTestSuite.java:72)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokePojoMethod(InvokerHelper.java:765)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:753)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:167)&lt;br /&gt;at TestApp_groovy$_run_closure8_closure18_closure19_closure20.doCall(TestApp_groovy:222)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:77)&lt;br /&gt;at TestApp_groovy$_run_closure8_closure18_closure19_closure20.doCall(TestApp_groovy)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.Closure.call(Closure.java:292)&lt;br /&gt;at groovy.lang.Closure.call(Closure.java:287)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:777)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:757)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:167)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeClosure(ScriptBytecodeAdapter.java:598)&lt;br /&gt;at TestApp_groovy$_run_closure10_closure27_closure28.doCall(TestApp_groovy:353)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.Closure.call(Closure.java:292)&lt;br /&gt;at org.codehaus.groovy.runtime.ConvertedClosure.invokeCustom(ConvertedClosure.java:48)&lt;br /&gt;at org.codehaus.groovy.runtime.ConversionHandler.invoke(ConversionHandler.java:72)&lt;br /&gt;at $Proxy20.doInTransaction(Unknown Source)&lt;br /&gt;at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:128)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokePojoMethod(InvokerHelper.java:765)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:753)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:167)&lt;br /&gt;at TestApp_groovy$_run_closure10_closure27.doCall(TestApp_groovy:365)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:77)&lt;br /&gt;at TestApp_groovy$_run_closure10_closure27.call(TestApp_groovy)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:777)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:757)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:167)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeClosure(ScriptBytecodeAdapter.java:598)&lt;br /&gt;at TestApp_groovy$_run_closure8_closure18_closure19.doCall(TestApp_groovy:220)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.Closure.call(Closure.java:292)&lt;br /&gt;at groovy.lang.Closure.call(Closure.java:305)&lt;br /&gt;at org.codehaus.groovy.runtime.DefaultGroovyMethods.withStream(DefaultGroovyMethods.java:8161)&lt;br /&gt;at org.codehaus.groovy.runtime.DefaultGroovyMethods.withOutputStream(DefaultGroovyMethods.java:7738)&lt;br /&gt;at org.codehaus.groovy.runtime.metaclass.ReflectionMetaMethod.invoke(ReflectionMetaMethod.java:51)&lt;br /&gt;at org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod.invoke(NewInstanceMetaMethod.java:54)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokePojoMethod(InvokerHelper.java:765)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:753)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:167)&lt;br /&gt;at TestApp_groovy$_run_closure8_closure18.doCall(TestApp_groovy:195)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.Closure.call(Closure.java:292)&lt;br /&gt;at groovy.lang.Closure.call(Closure.java:305)&lt;br /&gt;at org.codehaus.groovy.runtime.DefaultGroovyMethods.withStream(DefaultGroovyMethods.java:8161)&lt;br /&gt;at org.codehaus.groovy.runtime.DefaultGroovyMethods.withOutputStream(DefaultGroovyMethods.java:7738)&lt;br /&gt;at org.codehaus.groovy.runtime.metaclass.ReflectionMetaMethod.invoke(ReflectionMetaMethod.java:51)&lt;br /&gt;at org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod.invoke(NewInstanceMetaMethod.java:54)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokePojoMethod(InvokerHelper.java:765)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:753)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:167)&lt;br /&gt;at TestApp_groovy$_run_closure8.doCall(TestApp_groovy:194)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:77)&lt;br /&gt;at TestApp_groovy$_run_closure8.call(TestApp_groovy)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:777)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:757)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:167)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeClosure(ScriptBytecodeAdapter.java:598)&lt;br /&gt;at TestApp_groovy$_run_closure10.doCall(TestApp_groovy:338)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.DelegatingMetaClass.invokeMethod(DelegatingMetaClass.java:142)&lt;br /&gt;at org.codehaus.gant.GantMetaClass.invokeMethod(GantMetaClass.java:79)&lt;br /&gt;at org.codehaus.gant.GantMetaClass.invokeMethod(GantMetaClass.java:94)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:77)&lt;br /&gt;at TestApp_groovy$_run_closure10.doCall(TestApp_groovy)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.DelegatingMetaClass.invokeMethod(DelegatingMetaClass.java:142)&lt;br /&gt;at org.codehaus.gant.GantMetaClass.invokeMethod(GantMetaClass.java:79)&lt;br /&gt;at groovy.lang.Closure.call(Closure.java:292)&lt;br /&gt;at groovy.lang.Script.invokeMethod(Script.java:87)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethodOnGroovyObject(MetaClassImpl.java:934)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:881)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.DelegatingMetaClass.invokeMethod(DelegatingMetaClass.java:142)&lt;br /&gt;at org.codehaus.gant.GantMetaClass.invokeMethod(GantMetaClass.java:79)&lt;br /&gt;at org.codehaus.gant.GantMetaClass.invokeMethod(GantMetaClass.java:94)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:77)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrent0(ScriptBytecodeAdapter.java:109)&lt;br /&gt;at TestApp_groovy$_run_closure3.doCall(TestApp_groovy:116)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.DelegatingMetaClass.invokeMethod(DelegatingMetaClass.java:142)&lt;br /&gt;at org.codehaus.gant.GantMetaClass.invokeMethod(GantMetaClass.java:79)&lt;br /&gt;at org.codehaus.gant.GantMetaClass.invokeMethod(GantMetaClass.java:94)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:77)&lt;br /&gt;at TestApp_groovy$_run_closure3.doCall(TestApp_groovy)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.DelegatingMetaClass.invokeMethod(DelegatingMetaClass.java:142)&lt;br /&gt;at org.codehaus.gant.GantMetaClass.invokeMethod(GantMetaClass.java:79)&lt;br /&gt;at groovy.lang.Closure.call(Closure.java:292)&lt;br /&gt;at groovy.lang.Script.invokeMethod(Script.java:87)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethodOnGroovyObject(MetaClassImpl.java:934)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:881)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.DelegatingMetaClass.invokeMethod(DelegatingMetaClass.java:142)&lt;br /&gt;at org.codehaus.gant.GantMetaClass.invokeMethod(GantMetaClass.java:79)&lt;br /&gt;at org.codehaus.gant.GantMetaClass.invokeMethod(GantMetaClass.java:94)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:77)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrent0(ScriptBytecodeAdapter.java:109)&lt;br /&gt;at TestApp_groovy$_run_closure1.doCall(TestApp_groovy:62)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.DelegatingMetaClass.invokeMethod(DelegatingMetaClass.java:142)&lt;br /&gt;at org.codehaus.gant.GantMetaClass.invokeMethod(GantMetaClass.java:79)&lt;br /&gt;at org.codehaus.gant.GantMetaClass.invokeMethod(GantMetaClass.java:94)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:77)&lt;br /&gt;at TestApp_groovy$_run_closure1.doCall(TestApp_groovy)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.DelegatingMetaClass.invokeMethod(DelegatingMetaClass.java:142)&lt;br /&gt;at org.codehaus.gant.GantMetaClass.invokeMethod(GantMetaClass.java:79)&lt;br /&gt;at groovy.lang.Closure.call(Closure.java:292)&lt;br /&gt;at groovy.lang.Closure.call(Closure.java:287)&lt;br /&gt;at groovy.lang.Closure.run(Closure.java:368)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.DelegatingMetaClass.invokeMethod(DelegatingMetaClass.java:142)&lt;br /&gt;at org.codehaus.gant.GantMetaClass.invokeMethod(GantMetaClass.java:79)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:777)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:757)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:167)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethod0(ScriptBytecodeAdapter.java:195)&lt;br /&gt;at gant.Gant.dispatch(Gant.groovy:271)&lt;br /&gt;at gant.Gant.this$2$dispatch(Gant.groovy)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:675)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:777)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:757)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:167)&lt;br /&gt;at gant.Gant.invokeMethod(Gant.groovy)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:77)&lt;br /&gt;at gant.Gant.processTargets(Gant.groovy:436)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:77)&lt;br /&gt;at gant.Gant.processArgs(Gant.groovy:372)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:899)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeMethod(ExpandoMetaClass.java:946)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:740)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokePogoMethod(InvokerHelper.java:777)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:757)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:167)&lt;br /&gt;at org.codehaus.groovy.grails.cli.GrailsScriptRunner.callPluginOrGrailsScript(GrailsScriptRunner.groovy:204)&lt;br /&gt;at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)&lt;br /&gt;at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:226)&lt;br /&gt;at groovy.lang.MetaClassImpl.invokeStaticMethod(MetaClassImpl.java:1094)&lt;br /&gt;at groovy.lang.ExpandoMetaClass.invokeStaticMethod(ExpandoMetaClass.java:957)&lt;br /&gt;at org.codehaus.groovy.runtime.InvokerHelper.invokeStaticMethod(InvokerHelper.java:800)&lt;br /&gt;at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeStaticMethodN(ScriptBytecodeAdapter.java:212)&lt;br /&gt;at org.codehaus.groovy.grails.cli.GrailsScriptRunner.main(GrailsScriptRunner.groovy:124)&lt;br /&gt;at org.codehaus.groovy.grails.cli.support.GrailsStarter.rootLoader(GrailsStarter.java:140)&lt;br /&gt;at org.codehaus.groovy.grails.cli.support.GrailsStarter.main(GrailsStarter.java:169)&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/4049835707454928147/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2008/07/switch-your-test-to-groovy-maybe-not.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/4049835707454928147'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/4049835707454928147'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2008/07/switch-your-test-to-groovy-maybe-not.html' title='Switch your test to Groovy ? Maybe not (yet).'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-6127277507107329490</id><published>2008-05-15T02:41:00.017-04:00</published><updated>2008-12-10T03:50:48.859-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="netbeans"/><category scheme="http://www.blogger.com/atom/ns#" term="tapestry"/><title type='text'>Tapestry5 NetBeans Quickstart</title><content type='html'>I have been following the development of Tapestry 5 closely for the last couple of months, and I even got the first Tapestry 5 book that came out. I&#39;m a big Tapestry fan, and I&#39;ve been looking forward to the new Tapestry 5 release. I even lucked out and had a chance to talk to &lt;a href=&quot;http://tapestryjava.blogspot.com/&quot;&gt;Howard&lt;/a&gt; in person:&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/AVvXsEi70WXolknprscO3wvEronbeeVF3n-Fnz-G3NRaZ8mdc1jbQUALE8Il6PoI604FyrqF4K3rZhfpdRxs1qeqJvhyphenhyphen50BtzsVNRJUbfkprii9Sx7he3OsGd_JAH0cPNdfA1HmXZrJx3g/s1600-h/nfjs_howard.jpg&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/AVvXsEi70WXolknprscO3wvEronbeeVF3n-Fnz-G3NRaZ8mdc1jbQUALE8Il6PoI604FyrqF4K3rZhfpdRxs1qeqJvhyphenhyphen50BtzsVNRJUbfkprii9Sx7he3OsGd_JAH0cPNdfA1HmXZrJx3g/s400/nfjs_howard.jpg&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5200494571718168898&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;One of the cool things that keeps drawing me towards Tapestry is the goal to make it really easy and intuitive to work with. One of the goals that I remember hearing for Tapestry 5 was to make it that easy, that it would compete more w/ Grails &amp;amp; Rails and not so much w/ traditional Java Web app frameworks (e.g. Struts, Struts2, etc)&lt;br /&gt;&lt;br /&gt;However, when I first took a look at Tapestry 5, I was a little disappointed by the six line Maven command that one had to type in when starting a new project (e.g. see http://tapestry.apache.org/tapestry5/tutorial1/first.html):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mvn archetype:create&lt;br /&gt;-DarchetypeGroupId=org.apache.tapestry&lt;br /&gt;-DarchetypeArtifactId=quickstart&lt;br /&gt;-DgroupId=org.apache.tapestry&lt;br /&gt;-DartifactId=tutorial1&lt;br /&gt;-DpackageName=org.apache.tapestry.tutorial&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Compare that with Grails:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-style: italic;&quot;&gt;grails create-app&lt;br /&gt;&lt;br /&gt;Welcome to Grails 1.0 - http://grails.org/&lt;br /&gt;Licensed under Apache Standard License 2.0&lt;br /&gt;Grails home is set to: /usr/local/java/grails&lt;br /&gt;Application name not specified. Please enter:&lt;br /&gt;FooApp&lt;br /&gt;---------------&lt;br /&gt;[propertyfile] Updating property file: /home/polrtex/temp/FooApp/application.properties&lt;br /&gt;Created Grails Application at /home/polrtex/temp/FooApp&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It is true that one doesn&#39;t create an application all that often, and copy-and-pasting&lt;br /&gt;these initial 5-6 lines would not be that big of a deal. However, for a newcomer, the Tapestry 5 experience has to be really smooth and starting a new app should be a breeze.&lt;br /&gt;&lt;br /&gt;So, you would say, what does this have to do with NetBeans ?&lt;br /&gt;&lt;br /&gt;Here it comes. As usual, NetBeans rocks everyone&#39;s socks, by lowering the entry barrier into starting an application. Here are the first steps (equivalent to the first two pages of the Tapestry tutorial : http://tapestry.apache.org/tapestry5/tutorial1/env.html). Here is what you need to do:&lt;br /&gt;( Assuming that you already have a good install of NetBeans 6.1).&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Install the NetBeans Maven plugin by going into Tools -&gt; Plugins . Type &quot;maven&quot; in the upper-left corner search box and install the plugin. The result should look something like this:&lt;pre&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidzqXCb0MFEGKJlOIpQd289UzsU1ji_8jt4Qsl-m-vxNh3SJW2usmLQtcm7JZlHWZXyySqOIG9u5UJp5LV-BHTHb_b0x13aL187jNyz7YhQ4Al1bISkt9TBy-jrLZpDno4Nxg5jw/s1600-h/t5_netbeans_maven_plugin.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/AVvXsEidzqXCb0MFEGKJlOIpQd289UzsU1ji_8jt4Qsl-m-vxNh3SJW2usmLQtcm7JZlHWZXyySqOIG9u5UJp5LV-BHTHb_b0x13aL187jNyz7YhQ4Al1bISkt9TBy-jrLZpDno4Nxg5jw/s400/t5_netbeans_maven_plugin.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5200497075684102498&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;/pre&gt; &lt;/li&gt;&lt;li&gt;Create a new Maven project   &lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_UpiDUjvj6hLwpk8JyDqCSydWKQtfhoXx_M37g_rPZN-XbphJNkvNYvfvHFf5DwN5YH0388_ZR-ILl92FY9XLKO3S1hO-g_Zwkm5PkgrrY50jlixmWt49cJDNt1ZREQQykDVYDQ/s1600-h/t5_netbeans_getting_new_project_pg1.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/AVvXsEj_UpiDUjvj6hLwpk8JyDqCSydWKQtfhoXx_M37g_rPZN-XbphJNkvNYvfvHFf5DwN5YH0388_ZR-ILl92FY9XLKO3S1hO-g_Zwkm5PkgrrY50jlixmWt49cJDNt1ZREQQykDVYDQ/s400/t5_netbeans_getting_new_project_pg1.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5200498196670566770&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Expand the &quot;Archetypes from remote Maven repositories&quot; and scroll down to find the Tapestry5 quickstart archetype . You might want to pay close attention to the description and select the latest archetypes (in my case, for 5.0.11)&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixgG8tjLSJrttKrS6zytxys0zFE59vf2weIDczkBZzB69-LzGXTluttXl-OSatsGA8jXuuRP-EJgaADickjtmRNOSFmXgk2s83PW2-ry51PKRmE9A2L2AGcUmhoSrLLPgjiANGEg/s1600-h/t5_netbeans_getting_new_project_pg2.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/AVvXsEixgG8tjLSJrttKrS6zytxys0zFE59vf2weIDczkBZzB69-LzGXTluttXl-OSatsGA8jXuuRP-EJgaADickjtmRNOSFmXgk2s83PW2-ry51PKRmE9A2L2AGcUmhoSrLLPgjiANGEg/s400/t5_netbeans_getting_new_project_pg2.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5200499184513044866&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Now that you have the nice wizard, fill out the configuration attributes to specify the group id, the location of the project, the name of the project, etc. &lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwx2_qLLDedpPm_9OCa4v68FRTZLqeRDH2ZW4iI_rn6N5wFJ77RXwvTDC9lmd_oanivqwbkHqrjWA_ra1PRfKgnfF3ZqDzs1-fim-Ghp2iaQ1J7NgyTLTrl7s4JiL05DwcvZep7g/s1600-h/t6_netbeans_getting_new_project_pg3.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/AVvXsEgwx2_qLLDedpPm_9OCa4v68FRTZLqeRDH2ZW4iI_rn6N5wFJ77RXwvTDC9lmd_oanivqwbkHqrjWA_ra1PRfKgnfF3ZqDzs1-fim-Ghp2iaQ1J7NgyTLTrl7s4JiL05DwcvZep7g/s400/t6_netbeans_getting_new_project_pg3.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5200499691319185810&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;When you hit Finish, Maven will start downloading all the dependencies and setting up your project.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Your new and shiny project is ready to go. Go into the project Properties and select your desired server to deploy to:&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhS8jGrb8o4_Af1KdT6ExhekYsr2cNqC-9NIC44fsIukyYBpySt-XZmyEPZ6Pzt4XsfdYYR88sTiKmJ866fIlazG3BjaAPQ_eqEdLlE-KZN_8R20y1obSBmpVu1Wz5k2k19PF2AxA/s1600-h/t5_netbeans_select_server.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/AVvXsEhS8jGrb8o4_Af1KdT6ExhekYsr2cNqC-9NIC44fsIukyYBpySt-XZmyEPZ6Pzt4XsfdYYR88sTiKmJ866fIlazG3BjaAPQ_eqEdLlE-KZN_8R20y1obSBmpVu1Wz5k2k19PF2AxA/s400/t5_netbeans_select_server.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5200500838075453858&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Run the project from the project context menu and you&#39;re up and running !!!! &lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiG6aOoRKoLaBuniFqA_7wVcv0zJxX6ORpWjGHF6bGE2wST3p5G4zA4SBTCskskXVH8jxO4UTTq39ph3U2ji6i_G-QX6bIKG0HOsrFVYgYEi4FssXdQWpkwNwZskg84vSgeRP2wpg/s1600-h/t9_netbeans_new_project_run0.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/AVvXsEiG6aOoRKoLaBuniFqA_7wVcv0zJxX6ORpWjGHF6bGE2wST3p5G4zA4SBTCskskXVH8jxO4UTTq39ph3U2ji6i_G-QX6bIKG0HOsrFVYgYEi4FssXdQWpkwNwZskg84vSgeRP2wpg/s320/t9_netbeans_new_project_run0.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5200501731428651458&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;One very cool option that makes the Tapestry 5 setup almost identical to the Grails setup is the ability to run the application in Jetty by going to the command line and running:&lt;br /&gt;mvn jetty:run&lt;br /&gt;&lt;br /&gt;The upside of doing this is that after you run it, there is no deployment step. Tapestry supports reloading of the page templates as well as the page classes. As a result, whenever you make a change to a page, you can save it, refresh the browser and see the changes . Similarly, when you make a change to a class, compile it and the changes are immediately visible to the application. Such a setup really cuts down on waiting for the app to deploy. &lt;br /&gt;&lt;br /&gt;So now, looking back a little bit, this setup is indeed very competitive w/ the Grails setup. In Grails, you would download the Grails distribution, and then would run the &quot;grails create-app&quot; command to create the new project. Grails would then prompt you for some properties (e.g. project name) and you&#39;re done. For Tapestry, it&#39;s quite similar; however, instead of downloading the Tapestry distribution, you would simply download Maven2 (or in the case of NetBeans, you would install the NetBeans plugin). Even better for Tapestry, if you already have Maven install, then starting a new app becomes a zero cost operation : you just specify the command line args (or better yet, use NetBeans to create the new app) and you&#39;re on your way, all dependencies already in place. &lt;br /&gt;&lt;br /&gt;So, so far it&#39;s a tie between Grails and Tapestry. Grals vs Tapestry = 1:1&lt;br /&gt;&lt;br /&gt;Enjoy your shiny and new Tapestry 5 app in NetBeans !!!</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/6127277507107329490/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2008/05/tapestry5-netbeans-quickstart.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/6127277507107329490'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/6127277507107329490'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2008/05/tapestry5-netbeans-quickstart.html' title='Tapestry5 NetBeans Quickstart'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi70WXolknprscO3wvEronbeeVF3n-Fnz-G3NRaZ8mdc1jbQUALE8Il6PoI604FyrqF4K3rZhfpdRxs1qeqJvhyphenhyphen50BtzsVNRJUbfkprii9Sx7he3OsGd_JAH0cPNdfA1HmXZrJx3g/s72-c/nfjs_howard.jpg" height="72" width="72"/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-2860095315227702766</id><published>2008-05-02T23:35:00.003-04:00</published><updated>2008-05-02T23:45:21.142-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="netbeans"/><category scheme="http://www.blogger.com/atom/ns#" term="plugins"/><title type='text'>Quick File Chooser in NetBeans 6.1</title><content type='html'>I&#39;m on a roll today, but I thought I&#39;d drop this tip as well.&lt;br /&gt;&lt;br /&gt;I&#39;m a big fan of the NetBeans Quick File Chooser plugin, it makes it oh-so-easy to open up a file without having to detach my fingers from the keyboard. So, if you Google for the plugin, you&#39;ll find &lt;a href=&quot;http://blogs.sun.com/tor/entry/netbeans_plugins_i_use_part1&quot;&gt;Tor&#39;s recommendation&lt;/a&gt;  of the same; however, you can&#39;t find it on the &lt;a href=&quot;http://plugins.netbeans.org/&quot;&gt;Plugin Center&lt;/a&gt; . So, what if you really wanted to use the Quick File Chooser, in NetBeans 6.1, what should you do ? In the past, I&#39;ve always copied the plugin with my netbeans preferences from the previous version that I&#39;ve used, and it&#39;s worked OK. However, I decided on a clean NetBeans install on my home laptop, and I couldn&#39;t get my hands on it.&lt;br /&gt;&lt;br /&gt;So, I followed a tip from &lt;a href=&quot;http://blogs.sun.com/octav/entry/bleeding_edge_plugins&quot;&gt;Octavian&#39;s blog&lt;/a&gt;    , added the Netbeans Latest Build update center, and ... voila, the Quick File Chooser is in the list of available plugins. Since it doesn&#39;t depend on any of the features of 6.1+ code, it works great in 6.1 as well.   &lt;a href=&quot;http://plugins.netbeans.org/&quot;&gt;&lt;span style=&quot;display: block;&quot; id=&quot;formatbar_Buttons&quot;&gt;&lt;span class=&quot;on&quot; style=&quot;display: block;&quot; id=&quot;formatbar_CreateLink&quot; title=&quot;Link&quot; onmouseover=&quot;ButtonHoverOn(this);&quot; onmouseout=&quot;ButtonHoverOff(this);&quot; onmouseup=&quot;&quot; onmousedown=&quot;CheckFormatting(event);FormatbarButton(&#39;richeditorframe&#39;, this, 8);ButtonMouseDown(this);&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/2860095315227702766/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2008/05/quick-file-chooser-in-netbeans-61.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/2860095315227702766'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/2860095315227702766'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2008/05/quick-file-chooser-in-netbeans-61.html' title='Quick File Chooser in NetBeans 6.1'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-8456863582435812162</id><published>2008-05-02T15:14:00.005-04:00</published><updated>2008-05-02T23:00:05.808-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="blogger"/><category scheme="http://www.blogger.com/atom/ns#" term="grails"/><category scheme="http://www.blogger.com/atom/ns#" term="groovy"/><title type='text'>Groovy HTML Encode</title><content type='html'>I kinda thought that in the past I had used a special Groovy method to encode something as HTML. I mostly need that when I need to post something to Blogger (which completely baffles me - why isn&#39;t there a better way of copy-and-pasting html/xml in blogger content ???) . I do know that in Grails, there are &lt;a href=&quot;http://grails.codehaus.org/Dynamic+Encoding+Methods&quot;&gt;a couple of special methods&lt;/a&gt; that you can use if you want to encode something as html, json, or something else. However, I&#39;m not using Grails right now, so that&#39;s not always the best option.&lt;br /&gt;&lt;br /&gt;After wasting 15 minutes trying to remember exactly how I did it in the past, I realized that I was using &lt;a href=&quot;http://commons.apache.org/lang/apidocs/org/apache/commons/lang/StringEscapeUtils.html&quot;&gt;StringEscapeUtils&lt;/a&gt; from Apache commons-lang. So, having remembered that, it just works like magic, here is what I use to convert my xml/html samples in order to post them on Blogger:&lt;br /&gt;&lt;br /&gt;this.class.classLoader.rootLoader.addURL(new File(&quot;/usr/local/java/grails-1.0/lib/commons-lang-2.1.jar&quot;).toURL())&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;xml = &#39;&#39;&#39;&lt;br /&gt;&amp;lt;module id=&amp;quot;id&amp;quot; version=&amp;quot;0.0.1&amp;quot; package=&amp;quot;package&amp;quot;&amp;gt; &amp;lt;/module&amp;gt;&lt;br /&gt;&#39;&#39;&#39;&lt;br /&gt;org.apache.commons.lang.StringEscapeUtils.escapeHtml(xml).split(&quot;\n&quot;).each() {println it }&lt;br /&gt;&lt;br /&gt;, which outputs the following :&lt;br /&gt;&amp;amp;lt;module id=&amp;amp;quot;id&amp;amp;quot; version=&amp;amp;quot;0.0.1&amp;amp;quot; package=&amp;amp;quot;package&amp;amp;quot;&amp;amp;gt; &amp;amp;lt;/module&amp;amp;gt;&lt;br /&gt;&lt;br /&gt;, which is what I paste into blogger and it works like magic !!!&lt;br /&gt;&lt;br /&gt;No more running around like a headless chicken trying to find some service online that will convert my html/xml snippets !&lt;br /&gt;&lt;br /&gt;&lt;formatter usefile=&quot;false&quot; type=&quot;brief&quot;&gt;&lt;br /&gt;&lt;/formatter&gt;</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/8456863582435812162/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2008/05/groovy-html-encode.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/8456863582435812162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/8456863582435812162'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2008/05/groovy-html-encode.html' title='Groovy HTML Encode'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-3166605318957869556</id><published>2008-05-02T13:26:00.009-04:00</published><updated>2008-12-10T03:50:49.126-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="junit"/><category scheme="http://www.blogger.com/atom/ns#" term="netbeans"/><title type='text'>Freeform projects NetBeans JUnit test results : binding output to source code</title><content type='html'>As I had mentioned in a &lt;a href=&quot;http://www.troymaxventures.com/2007/05/junit-results-in-free-form-projects.html&quot;&gt;prior post&lt;/a&gt; a while back, it is pretty straightforward to bind the output of a Freeform Project JUnit task to the NetBeans JUnit test results. However, there are a couple of minor tweaks that I find myself making and forgetting, so, I thought I&#39;d drop a blog entry, even just as a reminder for myself.&lt;br /&gt;&lt;br /&gt;First, make sure that the JUnit task as a showoutput=&quot;true&quot; attribute and has a formatter that explicitly states that doesn&#39;t use a file, e.g.&lt;br /&gt;&lt;br /&gt;&amp;lt;&lt;span style=&quot;font-weight: bold;&quot;&gt;formatter usefile=&quot;false&quot; &lt;/span&gt;type=&quot;brief&quot;/&amp;gt;&lt;br /&gt;&lt;br /&gt;Emphasis on not using a file : e.g. you can use an xml formatter and it would work; however, if you don&#39;t specify the usefile=&quot;false&quot; attribute, everything goes to the file and NetBeans doesn&#39;t get a chance to capture the output and display the results in a nice JUnit test results tree. As a result, the best combination ends up being a combination of a xml formatter that outputs to a file, and a brief formatter that doesn&#39;t, e.g. :&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;junit fork=&quot;yes&quot; printsummary=&quot;withOutAndErr&quot; &lt;span style=&quot;font-weight: bold;&quot;&gt;showoutput=&quot;true&quot;&lt;/span&gt;                  errorProperty=&quot;test.failed&quot;            failureProperty=&quot;test.failed&quot; filtertrace=&quot;false&quot;&amp;gt;&lt;br /&gt;         &amp;lt;formatter type=&quot;xml&quot;&amp;gt;&lt;br /&gt;         &lt;span style=&quot;font-weight: bold;&quot;&gt;&amp;lt;formatter usefile=&quot;false&quot; type=&quot;brief&quot; /&amp;gt;&lt;/span&gt;&lt;br /&gt;         &amp;lt;classpath refid=&quot;whatever-path-id&quot;&amp;gt;&lt;br /&gt;&amp;lt;/classpath&amp;gt;&lt;br /&gt;&amp;lt;/junit&amp;gt;&lt;br /&gt;&lt;br /&gt;The second important part of working with the JUnit results in NetBeans is to make sure that when a test fails, when you click on the failure in the JUnit results, you want NetBeans to take you to the right line in the source code:&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiecSDyNKkhscWMMIYurs2tyJLq4yYdlCBXxmvUZwOSp5Krcg8LP7Yjoa4IfTh4Dl1r62dDglZPykcRwYpx8PId_2Yink3VyDLWgBasH-qYcbgH_u41l60fTm937k_O04uzmDFClg/s1600-h/netbeans_junit_beauty.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/AVvXsEiecSDyNKkhscWMMIYurs2tyJLq4yYdlCBXxmvUZwOSp5Krcg8LP7Yjoa4IfTh4Dl1r62dDglZPykcRwYpx8PId_2Yink3VyDLWgBasH-qYcbgH_u41l60fTm937k_O04uzmDFClg/s320/netbeans_junit_beauty.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5195835563047614786&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;In order to accomplish that, make sure that you properly set the output directory for your test cases in your NetBeans project with the UI or project.xml:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In the Project Properties UI&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPhZkItaJcmR3ATmQz9vscnNs1V2Y5dDd8KdaOCjSuJoNwfkqBOs-JsDWn7n59dKkerb4AEtYQ2vqK0nrGOOS5PvShyphenhyphenBpMTSY-IRiHO9FrYAUhCNlHFiqJNibGf4_jbfM-AW_YvA/s1600-h/junit_output_project_props.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/AVvXsEhPhZkItaJcmR3ATmQz9vscnNs1V2Y5dDd8KdaOCjSuJoNwfkqBOs-JsDWn7n59dKkerb4AEtYQ2vqK0nrGOOS5PvShyphenhyphenBpMTSY-IRiHO9FrYAUhCNlHFiqJNibGf4_jbfM-AW_YvA/s320/junit_output_project_props.png&quot; alt=&quot;&quot; id=&quot;BLOGGER_PHOTO_ID_5195860383663618386&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In the nbproject/project.xml&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;...&lt;br /&gt;&amp;lt;java-data xmlns=&quot;http://www.netbeans.org/ns/freeform-project-java/2&quot;&amp;gt;\n            ....&lt;br /&gt;&amp;lt;compilation-unit&amp;gt;&lt;br /&gt; &amp;lt;package-root&amp;gt;test/integration&amp;lt;/package-root&amp;gt;&lt;br /&gt;          &amp;lt;unit-tests/&amp;gt;&lt;br /&gt; &amp;lt;classpath mode=&quot;compile&quot;&amp;gt;${test.completion.classpath}&amp;lt;/classpath&amp;gt;&lt;br /&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;     &amp;lt;built-to&amp;gt;dest/test/unit&amp;lt;/built-to&amp;gt;&lt;/span&gt;&lt;br /&gt; &amp;lt;source-level&amp;gt;1.5&amp;lt;/source-level&amp;gt;&lt;br /&gt;&amp;lt;/compilation-unit&amp;gt;&lt;br /&gt;....&lt;br /&gt;&amp;lt;/java-data&amp;gt;&lt;br /&gt;....</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/3166605318957869556/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2008/05/freeform-projects-netbeans-junit-test.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/3166605318957869556'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/3166605318957869556'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2008/05/freeform-projects-netbeans-junit-test.html' title='Freeform projects NetBeans JUnit test results : binding output to source code'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiecSDyNKkhscWMMIYurs2tyJLq4yYdlCBXxmvUZwOSp5Krcg8LP7Yjoa4IfTh4Dl1r62dDglZPykcRwYpx8PId_2Yink3VyDLWgBasH-qYcbgH_u41l60fTm937k_O04uzmDFClg/s72-c/netbeans_junit_beauty.png" height="72" width="72"/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-8609304366187317750</id><published>2008-04-26T14:08:00.017-04:00</published><updated>2008-05-02T23:46:47.722-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="antivirus"/><category scheme="http://www.blogger.com/atom/ns#" term="rants"/><category scheme="http://www.blogger.com/atom/ns#" term="symantec"/><title type='text'>Symantec Horror Story : &quot;The Symantec Experience&quot;</title><content type='html'>After a 6 months hiatus in the antivirus subscription of one of the laptops at the house, I finally decided to bite the bullet and either get the upgrade or subscribe to the antivirus updates. I was quite reluctant to do it for quite a bit ( I just absolutely hate being blackmailed into buying a product), but after listening to &lt;a href=&quot;http://www.grc.com/securitynow.htm&quot;&gt;Security Now&lt;/a&gt;, I finally decided that it&#39;s not worth risking a security breach of the windows machine (btw, I&#39;m entirely embarrassed to admit that I do own a Windows machine) .  &lt;a href=&quot;http://tandoorisjourney.blogspot.com/&quot;&gt;My Better Half&lt;/a&gt; is too attached to her ICQ client (despite the million better alternatives) and a few other pieces of software that she never uses, and so far I haven&#39;t been able to convince her to switch to Linux. On the positive side, my son has been a Linux user since he&#39;s 3 years old and he&#39;s happily playing a whole bunch of nice games like SuperTux, PPRacer, and many more others..).&lt;br /&gt;&lt;br /&gt;Anyway, I digress. So, I finally decided to bit the bullet and part with 50 of my hard earned dollars. So, I go to the Symantec website (I did have a trial version that came with my access point), and here my woes really started.&lt;br /&gt;&lt;br /&gt;First, while I&#39;m ordering the software, they try to sell me &quot;A Service&quot; where they&#39;d keep my download for a year. I figured, hey, I could download it and burn it to a CD, and I don&#39;t need to shell out $10.  So , I happily click on the link and download an executable, I get a setup.exe and I start it up. Lo and behold, it&#39;s not the software, it&#39;s a &quot;Download Manager&quot; !!! In a later part of the saga, I asked their tech support what the service is all about and I can store the little download manager and reinstall the software 6 months from now, and they gladly told me that I could just download the trial version and use my registration code and I&#39;d be good. So, WHY did they recommend the &quot;Download Service&quot; for 10 bucks ??? Hm, first signs of fishiness start showing...&lt;br /&gt;&lt;br /&gt;Alright, so I downloaded the software, and started it up. As I mentioned before, the download manager started doing its job and in about half an hour the whole deal was downloaded. Now, while I was waiting for half an hour for the download to finish, why not make myself useful and see if I can clean up some of the old software from the machine. At one  time the Norton Internet antivirus decided that it was done downloading and it has to start installing &lt;b&gt;now&lt;/b&gt;! Well, as bad luck would have it, at the same time I was uninstalling an older JRE version. So, Norton decides to die and tell me that it can&#39;t install while something else is installing. I said fine, continued w/ my JRE uninstall, thinking that, stupid me, I&#39;ll just restart the Download Manager and it will resume the install.&lt;br /&gt;&lt;br /&gt;Not that easy !!! I start the setup.exe again and it just tells me that the download is finished and just closes. WHAT?!!??? Did I just pay fifty bucks for &lt;b&gt;this&lt;/b&gt;?? I didn&#39;t exactly order a download manager, I just wanted the damn software, but hey, they&#39;re smart and decided to give me a download manager instead. Alright, that works too, but at least make sure that the download manager knows how to start the installer, puh-puh-please !&lt;br /&gt;&lt;br /&gt;So, next action. What can I do ? Phone, internet suport forum, online chat ? I decide to go with the online chat. However, Symantec decides to be fresh again ! Instead of just firing up some AJAX little gizmo to just exchange a couple of words with their technician, what do I have to do ? Of course, I have to download and install an application, that would install and register an ActiveX in my system, so that when I go into their &quot;live chat&quot; feature, it can look as if it is running in the web browser. Not only that, but the little piece of crappy software only happens to work in Internet Explorer !!! Is this coming from a company that is claiming that will protect my security ? &quot;Oh, just install this little piece of software, we&#39;re just going to chat, disregard all the security warnings about ActiveX and such&quot;. What a bunch of losers !!&lt;br /&gt;&lt;br /&gt;So, I once again stoop to their level and download the ActiveX and install the thing. By now, I&#39;m quite pissed, I&#39;m running IE, allowing ActiveX garbage on my machine. I finally get to the chat page, and you&#39;d think with the ActiveX it would be something really fancy and slick. Nope. Just a text field for me to enter my comments, and a text area for the current conversation. The crown jewel of this work of art are three radio buttons to select what the technican can do &quot;Nothing&quot;, &quot;View Only&quot; and &quot;Full Control&quot;. You might think that a security company would be concerned and would give the users some choice to protect themselves . No such luck. The default is on &quot;Full Control&quot;. And I thought that best practice in security would have been to give the user a choice and let them choose if someone else would poke around their machine, and worse off, I thought Symantec would have thought of that. Another level of disappointment reached !&lt;br /&gt;&lt;br /&gt;So, I start talking to the dude (Krishnan) who takes his sweet time to answer my questions. I type in 5 things, wait for 10 minutes and only then he decides that I&#39;m worth his attention. First, I ask him a couple of things about how disappointing the experience is so far, and then I get to the meat of things : I ask him why I can&#39;t install the product. The solution turns out to be &quot;easy&quot; : I just go to the Symantec site and download and run a tool (yup, an .exe), and what do you think it does ?? Ta-da !! It cleans up whatever the Download Manager downloaded so that I can start the Download Manager again and let it download for another half hour. Isn&#39;t this brilliant ?? Anybody with a brain stem would have figured this out : hey, why not include the &quot;Download Cleanup&quot; functionality into the Download Manager ? I mean, it&#39;s not like they shipped the product to me 6 months ago and don&#39;t have a chance to patch in this functionality : I downloaded the Download Manager 10 minutes ago !&lt;br /&gt;&lt;br /&gt;Alright, next phase in the saga. I clean up my download, start the download again (yep, another 30 minutes down the drain) and I&#39;m chattling w/ Krishnan about how I can leave some feedback to Symantec to help them improve their product. I could certainly just bitch about it (like I&#39;m doing now) and and let them continue having a crappy product, but hey, I decided I&#39;ll give them a shout. So, while I&#39;m still chatting w/ the dude, I went to their feedback page (of course still in IE7) and then... IE CRASHES and BURNS !!! Now, I&#39;d guess it wasn&#39;t just a fault of IE, for some reason it&#39;s the ActiveX that I was using (of course, this is just speculation, I was so mad I didn&#39;t go digging through logs and such). Now I&#39;m just flipping out : I&#39;m using IE, ActiveX running, with a product that just goes out of its way trying to prevent me from using it, after being blackmailed for fifty bucks to run a shitty OS on my sweetheart&#39;s laptop, and it just died !!!&lt;br /&gt;&lt;br /&gt;At this point, I&#39;m lost for words. On one side, I could go and talk to tech support again, and possibly lose another hour dealing with them, or I could just try doing it on my own. But I&#39;m suffiiently disappointed and I really want to try and ask them if I can cancel my order. Note, I said &quot;ask them if I can&quot;, not &quot;tell them to cancel&quot;. So, I go back into their online chat support center (ActiveX and all), and I start talking to a new dude (hm, was it Prasad??). I explained the problem that I had and I ask him to tell me what the options are to cancel my order. I specifically told him &quot;Don&#39;t cancel the order yet, just tell me what the options are&quot;. I give him address and order number to look up the info. Once again, Dude takes his time, no hurry for him, he&#39;s getting paid for his time. Just as I&#39;m about to ask him what&#39;s going on, he cheerfully informs me that... I&#39;m all set, my order is cancelled and that it might take a few days for the refund to process. WHAT ???!!?? I specifically told the dude, DON&#39;T cancel the order. Just as I&#39;m explaining to the dude that I didn&#39;t want to cancel the order, he disconnects and reconnects from the chat session a couple of times, not mentioning a thing about it (no sorry, I got disconnected, nothing).&lt;br /&gt;&lt;br /&gt;So, let me recap here. I&#39;m working in Windows, that sucks. I part with my money to fix the crummy OS and prevent it from being 0wn3d every other day, and the antivirus company tries to take my dough for a useless download service (by default), so I&#39;m even more bummed out. Then, I try to install something that should be a total no brainer, it doesn&#39;t care about installing the software, it cares about downloading it only. Hm... I&#39;m fuming ! Then, the security company makes me jump through 100 insecure hoops including installing ActiveX-s who want full control by default of my desktop, running executables just downloaded from the web (albeit from their site, supposedly secure), and giving full remote control to a dude I don&#39;t know sitting somewhere in India. I&#39;m starting to flip out now !!! How many times did I give somebody a chance to r00t my machine, I wonder ? Somewhere along the way, the antivirus company&#39;s software crashes my browser, and to top it off, the customer support people just blow me off, take their sweet time in addressing my issues, and in the end just cancel the order despite the fact that I told them not to !!!!&lt;br /&gt;&lt;br /&gt;So, I ask , WHY ? I can see that my fifty bucks are not going to break a billion dollar company (or however big it is, it is big). But is that a reason to totally dis me and let it be known that they don&#39;t care ? Is that a reason to introduce glaring security issues along the way (ActiveX, executables, giving full control to my box by default) just based on the fact that they are &quot;the security experts&quot; - e.g. what if someone compromised THEIR site, how many of their customers would be compromised along the way ? Is that a reason for the customer support to not pay attantion to what I&#39;m saying and disrespect me by just answering once every 5 minutes ?&lt;br /&gt;&lt;br /&gt;Anyway, that&#39;s my story. I just blows me away that such a large industry is built on the side of a bug ridden OS, and to make it all more perverse, the culprit of the whole situation is also in the same industry (e.g. Microsoft OneCare product charges for services that prevent attackers from exploiting bugs in the OS that Microsoft itself built). Now, wouldn&#39;t it make sense that if one bought a Microsoft product, such protection would come as a part of the OS, instead of selling you a faulty OS, and then selling you a service to fix it. It&#39;s an interesting conflict of interest : would Microsoft make more money if they fix the OS and make it less exploitable (thus losing money on Antivirus support), or would they make more money selling a crummy defect-ridden OS and then selling Antivirus products for it ?&lt;br /&gt;&lt;br /&gt;This is the end of my story. My conclusion : I&#39;m so glad I use Linux !! Rock on &lt;a href=&quot;http://www.fedoraproject.org/&quot;&gt;Fedora&lt;/a&gt;, I&#39;m looking forward to Fedora 9 !!</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/8609304366187317750/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2008/04/symantec-horror-story-symantec.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/8609304366187317750'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/8609304366187317750'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2008/04/symantec-horror-story-symantec.html' title='Symantec Horror Story : &quot;The Symantec Experience&quot;'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-9112326945869070527</id><published>2008-04-07T22:24:00.009-04:00</published><updated>2008-04-08T21:43:12.835-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="apple"/><category scheme="http://www.blogger.com/atom/ns#" term="iphone"/><category scheme="http://www.blogger.com/atom/ns#" term="rants"/><title type='text'>Apple Brainwashing puzzle</title><content type='html'>I write software for a living. As a result a lot of my favorite software developers at work and in my communities (e.g. Tapestry, Grails, NetBeans) are Mac users. On one hand, I accept the fact that, hey, they like their Mac, just as much as I am passionate and like my NetBeans for example. However, there are a couple of things that I haven&#39;t been able to grasp yet.&lt;br /&gt;&lt;br /&gt;All of the people I&#39;m talking about are very smart and very independent. Yet, at the same time, Apple has managed to run some kind of brainwashing trick, where anything Apple comes up with and anything Apple says is taken at face value. Here are a couple of glaring examples:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A year or however long time ago, Apple decides to do their iPhone thing. Great, nice device, cool glitzy graphics, all that. All of a sudden, everyone from a large number of my coworkers to the people on the Oscars buys an iPhone. That is all well, but the more puzzling part is how most people just swallow the garbage coming from Apple without questioning it. For example, I bet that 90% of the iPhone users still think that iPhone was the first device that had a full blown browser that doesn&#39;t need the content to be specifically formatted for mobile devices ( while I&#39;ve been happily browsing on my Nokia E61 for 1-2 years before the iPhone came out, mind you with the E61 happily running a KHTML derived browser, the same browser that Safari extends as well).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Apple decides to release their SDK and to allow 3rd party Devs to distribute their apps for iTunes. That is all great, nice thinking, good way to screw the walled garden of the network operators (ironically, by creating a new walled garden, this time controlled by Apple). So,  all of a sudden, everyone believes ( I spoke to a very smart coworker of mine, listened to the Java Posse) that this is the first attempt where a non-operator sets up a distribution mechanism where 3rd party devs can certify and distribute their apps. Rewind to 2-3 years before the iPhone SDK launch, and I was happily exploring the Nokia Catalogs (on my phone) that has a whole bunch of free and commercial applications for download (that have nothing to do with the operator, the same &quot;independent&quot; software distribution channel that Apple claims to have invented).&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;The list can go on and on. What really puzzles me is that Apple can throw these outrageously false claims, and all of these smart and independently thinking people that I know just eat it up, without questioning it for a second.  What drove me over the edge was when I was listening to a Java Posse interview where they were discussing the release of the iPhone SDK and how Apple supposedly &quot;owned&quot; the device and that Apple was within their right to lock the device as much as they want and go to great lengths to restrict what kind of software could be on it (e.g. starting from the &quot;locked&quot; phones, going through the clauses in the iPhone dev kit that prohibits its usage for creation of VMs, including Java). That is the biggest steaming pile of BS that I&#39;ve heard . Yeah, I know that when you get the software you don&#39;t actually &quot;own&quot; it, you license it, but I can&#39;t believe my ears : if I had spent $400-$500 on a device you better believe it that I will want to have as much control over it, I OWN the goddamn thing. Now, if it was someone other than Apple (e.g. think Microsoft), all of these smart people would be up in arms calling for boycotts of the device and the company and who knows what else. But when it&#39;s Apple, people just suck it up.&lt;br /&gt;&lt;br /&gt;So, people, let get our senses back and start thinking a little bit more critically about the garbage that comes out of Apple&#39;s brainwashing machine. I know they make nice products and all, but let&#39;s not lose our senses every time they say something and examine it for what it really is : a company pushing their products and very selfishly looking out for its own interests.</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/9112326945869070527/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2008/04/apple-brainwashing-puzzle.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/9112326945869070527'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/9112326945869070527'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2008/04/apple-brainwashing-puzzle.html' title='Apple Brainwashing puzzle'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-4102299623478871275</id><published>2008-02-28T10:25:00.003-05:00</published><updated>2008-02-28T12:31:58.138-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="j2me"/><category scheme="http://www.blogger.com/atom/ns#" term="my thesis"/><title type='text'>Thesis presentation</title><content type='html'>Below is the content of the presentation that I gave on my thesis about a year ago. It clearly is missing a lot of the content to explain what each slide is all about, but it nicely gives the general idea of what the solution is like:&lt;br /&gt;&lt;br /&gt;&lt;div xmlns=&#39;http://www.w3.org/1999/xhtml&#39;&gt;&lt;object codebase=&#39;http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0&#39; classid=&#39;clsid:D27CDB6E-AE6D-11cf-96B8-444553540000&#39; height=&#39;Height in Pixels&#39; width=&#39;Width in Pixels&#39;&gt;&lt;br /&gt; &lt;param value=&#39;lt&#39; name=&#39;salign&#39;/&gt;&lt;br /&gt; &lt;param value=&#39;high&#39; name=&#39;quality&#39;/&gt;&lt;br /&gt; &lt;param value=&#39;noscale&#39; name=&#39;scale&#39;/&gt;&lt;br /&gt; &lt;param value=&#39;transparent&#39; name=&#39;wmode&#39;/&gt;&lt;br /&gt; &lt;param value=&#39;http://ghs.troymaxventures.com/MVP_Presentation2.swf&#39; name=&#39;movie&#39;/&gt;&lt;br /&gt; &lt;embed wmode=&#39;transparent&#39; src=&#39;http://ghs.troymaxventures.com/MVP_Presentation2.swf&#39; pluginspage=&#39;http://www.macromedia.com/go/getflashplayer&#39; type=&#39;application/x-shockwave-flash&#39; salign=&#39;LT&#39; scale=&#39;noscale&#39; quality=&#39;high&#39; height=&#39;600&#39; width=&#39;800&#39;&gt;&lt;br /&gt; &lt;br /&gt; &lt;/embed&gt;&lt;br /&gt;&lt;/object&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/4102299623478871275/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2008/02/test-swf.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/4102299623478871275'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/4102299623478871275'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2008/02/test-swf.html' title='Thesis presentation'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-19648744.post-915393248666298624</id><published>2007-10-16T03:09:00.000-04:00</published><updated>2007-10-16T13:21:18.123-04:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="j2me"/><category scheme="http://www.blogger.com/atom/ns#" term="mobile video"/><category scheme="http://www.blogger.com/atom/ns#" term="my thesis"/><title type='text'>Starting to publish thesis</title><content type='html'>After a long time for thinking and deciding what I&#39;m doing with my thesis, I decided to go ahead and start publishing it little by little. Overall, I could post the whole thing in one swooop; however, I have to do some conversion into HTML; thus, I plan to publish it chapter by chapter. &lt;br /&gt;&lt;br /&gt;Here (as a test) is the content of the thesis abstract:&lt;br /&gt;&lt;br /&gt;&lt;iframe src=&quot;http://ghs.troymaxventures.com-a.googlepages.com/J2ME_Streaming_Inc_Abstract.html&quot; width=&quot;90%&quot; height=&quot;1200&quot;   name=&quot;content&quot;&gt;&lt;/iframe&gt;</content><link rel='replies' type='application/atom+xml' href='http://akochnev.blogspot.com/feeds/915393248666298624/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://akochnev.blogspot.com/2007/10/starting-to-publish-thesis.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/915393248666298624'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/19648744/posts/default/915393248666298624'/><link rel='alternate' type='text/html' href='http://akochnev.blogspot.com/2007/10/starting-to-publish-thesis.html' title='Starting to publish thesis'/><author><name>Alex Kochnev</name><uri>http://www.blogger.com/profile/05519996133043093264</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>