<?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: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-3956414281955169290</id><updated>2011-07-30T16:08:08.722-07:00</updated><category term='Random Thoughts'/><category term='Flex'/><category term='Grails'/><category term='CI'/><category term='JRuby'/><category term='Ruby on Rails'/><category term='Cool Tools'/><category term='Java'/><category term='Crowd'/><title type='text'>belongs_to :meagle</title><subtitle type='html'>Working harder to simplify software development</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>21</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-2458723900420489116</id><published>2009-08-20T14:42:00.000-07:00</published><updated>2009-08-20T14:44:56.963-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby on Rails'/><category scheme='http://www.blogger.com/atom/ns#' term='Grails'/><title type='text'>Choking on Java</title><content type='html'>As you can tell from my blog I am a long time Java developer who likes to flirt with dynamic languages.  Not surprisingly my two favorite languages at this point are Groovy and Ruby.  Therefore, I enjoy working with the Grails and Rails frameworks.  After working with Java for so long I was starting to get tunnel vision around a single language.  After all, Java is a general-purpose language that can do anything if you bend it enough. &lt;br /&gt;&lt;br /&gt;However, Java is not always the best solution for all situations.  Obviously, Web development is one of those glaring areas where Java can work but might not be the best candidate.  I began developing web applications with Java using plain old servlets and jsp pages.  Like most Java developers I also discovered Struts 1.  While some of the components of the framework seemed a little strange, Struts was better than my homegrown attempt.  So I developed with Struts for a while but I was not entirely satisfied.  Of course there were other issues to tackle outside of the presentation layer including persistence, transaction handling, and other services.  So I evaluated JDO, EJB, and Hibernate.  I finally settled on Hibernate but there was still a mess between my presentation tier and my persistence layer.  Later I found Spring and glued everything together.  After evaluating countless presentation frameworks I refined my Java Web stack over time using Stripes, Spring, and Hibernate.  This combination took a lot of experimentation but I was able to make Java conform to the web in a relatively lightweight manner.  The point is that I had to do a lot of bending here.&lt;br /&gt;&lt;br /&gt;When Rails first came on the scene I was fairly closed minded mainly because of my investment in Java.  How could this newcomer trump all my hard work or be as robust as Java?  Well after taking Rails out for a test drive I quickly realized how over-architected Web development was with Java.  It reminded me of so many early math classes where it would take several pages of computation to achieve a result.  Then more advanced math classes introduced techniques to achieve the same results in just a few steps.  I appreciated the simplicity of solving a problem with less computation and yielding the same results (or better).  Doing less work for the same results once I understand the underlying theory works for me. &lt;br /&gt;&lt;br /&gt;But with RoR there was a fair bit of voodoo so I became less intrigued by Rails and wanted to know as much as possible about Ruby.  Ruby turned out to be a fun language with constructs that are currently not available or easy to achieve with Java (i.e. closures, better mixin techniques, creating your own DSLs with builders, everything is an object, etc).  I quickly realized that I could code much more expressively and achieve the same functionality with a lot less lines of code.  Testing my code with a dynamic language instead of Java was also enjoyable because it was simpler.  I actually wanted to do testing!  Exposure to a new language like Ruby actually improved my Java code (and made me resent how noisy Java is).  So now I am an aspiring polyglot with an open mind to new languages.  &lt;br /&gt;&lt;br /&gt;Not long after I looked at Rails I saw that Grails 0.3 was available (I think I first saw it on http://java.net).  So the Java camp was attempting to answer Rails with Grails.  OK fine, I took a look at Grails and this is when I was first exposed to Groovy.  Groovy offered much of the same dynamic capabilities as Ruby and worked well with my existing Java code (mostly).  And Grails offered much of the same convention over configuration principles like Rails.  Even better I could integrate my Grails application with my existing Java code.  It was like winning the World Series of Poker when I hooked up my existing Spring beans and Hibernate annotated domain objects with Grails.  Grails automatically recognized my Spring beans and seamlessly added all of the GORM dynamic methods to my domain classes.  &lt;br /&gt;&lt;br /&gt;However, I still liked the Ruby language slightly better than Groovy because it did not carry the Java baggage along with it.  So now I have a language and a framework that are great for new development, but I could not reach out to my existing Java classes or external libraries with Ruby.  Then I found JRuby.  JRuby allows me to write Ruby code and integrate with my Java code, Spring beans, etc.  However, if I was going to use Rails and wanted to follow their paint by numbers framework then I was stuck with ActiveRecord.  Also Rails does not have a service layer concept so my Spring beans did not naturally fit in the Rails framework.  I quickly found out how to create a Spring bean factory in JRuby so my Rails controllers could access these services.&lt;br /&gt;&lt;br /&gt;As much as I like JRuby there are some things that I want.  First, I would like to see JRuby continue to strive for near Java performance.  I do realize that we are talking about a dynamic language but I believe that this can be achieved.  Second, I think that JRuby needs to provide easier integration for popular open source frameworks like Spring and Hibernate.  Why shouldn’t JRuby/Rails emulate Grails the same way that Grails “borrowed” from Rails.  Maybe the JRuby team could offer these items as gems to supplement the Rails framework.  Or maybe this will be a pluggable feature with Rails 3?  This is about bridging the gap between the Java and Ruby communities.  Finally, I want to see Engine Yard offer commercial support and training for JRuby (which seem inevitable now that Charles Nutter is at EY). &lt;br /&gt;&lt;br /&gt;While building Rails applications is fun, deployment can be a pain.  I think that the leveraging JRuby/Rails on a Java platform makes a lot of sense here.  Ruby contains several gems for integrating your Rails application in a Java environment.  First, there is the Warbler gem, which allows you to take your Rails application and package it up in a standard war file format.  So now your Rails application can be deployed on any standard Java container.  Second, there is a Glassfish gem for deploying your applications without Warbler.  The JDBC-ActiveRecord gem allows you to use your JDBC drivers to connect to your database instead of the Rails drivers.  By deploying your Rails application on a JVM you get extra benefits like simpler deployment strategies, better threading support, and easier ways to scale.  So while you might want Rails for development, the JVM might offer better services for housing your application.  Even if you don't have to integrate any Java code you should still consider JRuby for Rails development IMHO.&lt;br /&gt;&lt;br /&gt;So, if you choose to use a dynamic language should you use Grails or Rails for Web development?  I do not think there is a cut and dry answer to this question.  In this case, the lines are a little blurry because the underlying languages seem to be converging in capabilities.  However, I will provide my 2 cents on this issue and watch the comments for this entry fill up.  Let me start off by saying that I like both frameworks for different reasons. &lt;br /&gt;&lt;br /&gt;Let's start with Grails.  Grails is great for Java developers that have existing experience with Spring and Hibernate.  The migration to Grails will benefit these people the most because they understand how Spring and Hibernate work.  Grails is pretty much just a wrapper around these frameworks so when problems arise they will have the same issues that had to be solved before.  The benefit is that this is a great way to get started with a dynamic language (like Groovy) and start to understand what all the fuss is about using a familiar set of tools.  Plus, as I indicated earlier you could drop in your Spring beans and Hibernate classes and watch them work out of the box.  You will need a deep understanding of Spring and Hibernate when things go wrong to troubleshoot problems as they arise.  This is where things might break down for developers that do not understand these frameworks.  &lt;br /&gt;&lt;br /&gt;Now let's talk about Ruby on Rails with JRuby. Working in Rails is just a fun way to develop.  Rails was created solely to solve the domain specific problem around web development.  And Rails developers seem to introduce easy solutions to mundane problems.  There are lots of plugins and gems that can make Rails even more enjoyable.  However, there are thousands of Java libraries available that you might want or need to integrate with.  As a Rails developer why wouldn't you at least give yourself the option to tap into those libraries?    &lt;br /&gt;&lt;br /&gt;So how does everybody win?  I believe that everyone will benefit if these two communities come together.  I have observed the Java and Ruby communities for some time and seen a lot of mud thrown from both sides.  At the end of the day you have to ask yourself why these two groups cannot see that they really need each other at some level.  Is this more of a cultural gap than a technical one?  Java is infused into so many organizations and is not likely to go away any time soon.  What better way to get your Rails application deployed in a Java shop than making a case with JRuby?  Also, if you want to evangelize and strengthen Rails as a popular development platform why not build on top of one of the largest existing infrastructures?  Rails will only survive by gaining acceptance as a viable framework. &lt;br /&gt;&lt;br /&gt;As for Java developers that think that Rails is not a good solution for Web development I say they are passing judgment without entertaining the idea.  Let's face it, Java is legacy.  If you still think that JSF provides rapid development or think WebSphere is a great application server then you probably will not buy into this blog entry.  As technologists we need to find and accept better ways to solve problems.  Maybe Rails is not the end all solution for Web development.  There are many emerging frameworks like Seaside, Django, Lift, Sinatra, Camping and more that believe they have a better mousetrap.  This is a sign that people believe that Java is too complicated and over-architected for web development. &lt;br /&gt;&lt;br /&gt;Let me summarize by overstating how much Web development has changed.  Java developers and Ruby developers need to understand how the JVM can benefit them.  Web development using domain specific languages and dynamic languages are hot and over architected Java solutions are not.  But there is a middle ground and whether you choose Grails, RoR, or some other framework to simplify your web development you are probably on the right track.  Finally, both the Java community and the Ruby community have things to offer each other to secure their survival. &lt;br /&gt;&lt;br /&gt;Well off to look at more Scala...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-2458723900420489116?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/2458723900420489116/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=2458723900420489116' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/2458723900420489116'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/2458723900420489116'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2009/08/choking-on-java.html' title='Choking on Java'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-2523103917382683220</id><published>2009-07-01T04:04:00.000-07:00</published><updated>2009-07-01T04:11:29.834-07:00</updated><title type='text'>IntelliJ 8.1 buuild 9732 hangs with Apple Java for OS X 10.5 - Update 4</title><content type='html'>&lt;div class="jive-rendered-content"&gt;&lt;p&gt;If you are a Mac user and you applied Apple's Java for OSX 10.5 Update 4 you might be experiencing crashes with IntelliJ 8.1.x. It appears that the software was not tested well enough against the developer preview before the update went GA. You can read more about this here:&lt;/p&gt;&lt;p style="padding-left: 30px;"&gt;&lt;a class="jive-link-external-small" href="http://lists.apple.com/archives/java-dev/2009/Jun/msg00142.html"&gt;http://lists.apple.com/archives/java-dev/2009/Jun/msg00142.html&lt;/a&gt;&lt;/p&gt;&lt;p&gt;While you cannot rollback the Java update on the Mac there is a workaround for this issue as described here:&lt;/p&gt;&lt;p style="padding-left: 30px;"&gt;&lt;a class="jive-link-external-small" href="http://www.jetbrains.net/devnet/message/5240216"&gt;http://www.jetbrains.net/devnet/message/5240216&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Here are the steps I used to correct this on my Mac:&lt;/p&gt;&lt;p&gt;Rename this file:&lt;/p&gt;&lt;p style="padding-left: 30px; color: rgb(102, 102, 102);"&gt;/Applications/IntelliJ IDEA 8.1.3.app/Contents/MacOS/idea&lt;/p&gt;&lt;p&gt;Replace the idea file with the Java system file like this:&lt;/p&gt;&lt;p style="padding-left: 30px; color: rgb(102, 102, 102);"&gt;cp /System/Library/Frameworks/JavaVM.framework/Resources/MacOS/JavaApplicationStub 64 to /Applications/IntelliJ IDEA 8.1.3.app/Contents/MacOS/&lt;/p&gt;&lt;p&gt;This did correct the issue for me and the software has been stable. IntelliJ was not the only software affected by this update. I also had to get a new version of DbVisualizer (6.5.7) to correct issues with that software as well because of this issue.&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-2523103917382683220?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/2523103917382683220/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=2523103917382683220' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/2523103917382683220'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/2523103917382683220'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2009/07/intellij-81-buuild-9732-hangs-with.html' title='IntelliJ 8.1 buuild 9732 hangs with Apple Java for OS X 10.5 - Update 4'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-1671828420565572721</id><published>2009-06-02T07:37:00.000-07:00</published><updated>2009-06-02T08:12:43.575-07:00</updated><title type='text'>RSpec and AutoSpec with JRuby</title><content type='html'>I had a little bit of a struggle last weekend setting up &lt;a href="http://rspec.info/"&gt;RSpec&lt;/a&gt; with &lt;a href="http://jruby.codehaus.org/"&gt;JRuby&lt;/a&gt; so I thought I would share the way I set this up.  I am not sure why I could not find more information on how to do this so I thought I could help the next guy.  Anyways, I am using JRuby 1.3.0RC2 which comes bundled with RSpec 1.2.6 which is great but there were two issues I needed to address before using this in my Rails application.&lt;br /&gt;&lt;br /&gt;The first issue was introducing RSpec into my Rails application.  It turns out that this is simple.  First you need to install the RSpec-Rails gem like this:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;jruby -S gem install rpec-rails&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Next switch to your Rails project home directory and scaffold out the RSpec components like this:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;jruby -S script/generate rspec&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This will introduce an rspec folder with the initial scaffolding you will need to write RSpec tests for your Rails application.  This only needs to be done once.&lt;br /&gt;&lt;br /&gt;The next thing I wanted to do was use AutoTest so that my tests would run in the background while I am coding.  This way I can make changes to my code and tests and have the modified tests run on a continuous basis.  When we ran the RSpec scaffolding in the Rails application we received an autospec script in the script folder.  This is what will allow you to have this perform your local continuous testing.  The only other gem you will need is the ZenTest gem which is used for AutoTest and AutoSpec.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;jruby -S gem install ZenTest&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You are now setup to run all of your Rails RSpec tests manually with Rake from your project home directory:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;jruby -S rake spec&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Or you can turn on AutoSpec and let this happen in a Terminal like this:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 51, 255);"&gt;jruby -S script/autospec&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I am also using the latest NetBeans IDE (6.7 RC1) for development and find that AutoSpec works great there as well.  To do this right click on your project folder and click AutoSpec.  This will kick off the script.  Here is an example of some of my results:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;img src="file:///Users/meagle/Library/Caches/TemporaryItems/moz-screenshot.jpg" alt="" /&gt;&lt;br /&gt;&lt;/div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_T5piBwJWgOI/SiU-Nl6haPI/AAAAAAAAACQ/X0f10X7388w/s1600-h/Picture+1.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 105px;" src="http://3.bp.blogspot.com/_T5piBwJWgOI/SiU-Nl6haPI/AAAAAAAAACQ/X0f10X7388w/s400/Picture+1.png" alt="" id="BLOGGER_PHOTO_ID_5342744936181754098" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;NetBean also has a nice navigator window for seeing an outline of your specs:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_T5piBwJWgOI/SiU_IV_hqWI/AAAAAAAAACY/-Gu2PKm3Dak/s1600-h/Picture+2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 386px; height: 309px;" src="http://2.bp.blogspot.com/_T5piBwJWgOI/SiU_IV_hqWI/AAAAAAAAACY/-Gu2PKm3Dak/s400/Picture+2.png" alt="" id="BLOGGER_PHOTO_ID_5342745945520056674" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I think that RSpec and AutoSpec provides a very easy and productive way to practice TDD in your Rails applications.  Well back to writing more tests...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-1671828420565572721?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/1671828420565572721/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=1671828420565572721' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/1671828420565572721'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/1671828420565572721'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2009/06/rspec-and-autospec-with-jruby.html' title='RSpec and AutoSpec with JRuby'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_T5piBwJWgOI/SiU-Nl6haPI/AAAAAAAAACQ/X0f10X7388w/s72-c/Picture+1.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-337792172352205090</id><published>2009-03-27T18:39:00.000-07:00</published><updated>2009-03-27T19:01:57.766-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Random Thoughts'/><title type='text'>I Code Better Without A Computer</title><content type='html'>Alright so this might sound a little silly because we need a computer to actually write code.  However, I find that most of my best design, code, and refactoring efforts happen when I am not at a computer.  This has been a common theme for me since college where I would wake up with a thought about some problem I was working on but could not resolve while conscious.  I would literally wake up some nights with an answer to something that I obsessed about during the day.&lt;br /&gt;&lt;br /&gt;I continue to find myself doing the same thing with my code.  This happens when I am driving, sleeping, watching TV, playing WoW, showering (don't think too hard on that one) or while my wife is trying to carry on an important conversation (bad idea BTW).  I just tell her that I was listening but spawned a new thread while she was talking.  This tends to not work in my best interest so don't try that one.  I will let you know when I think of a response that won't get you in trouble.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://neuronarrative.files.wordpress.com/2008/11/left-brain-right-brain.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 241px; height: 255px;" src="http://neuronarrative.files.wordpress.com/2008/11/left-brain-right-brain.jpg" alt="" border="0" /&gt;&lt;/a&gt;I guess I found out that I was not the only person who experiences this behavior when I attended Neal Ford's &lt;span&gt;&lt;a href="http://www.nealford.com/downloads/conferences/Neal_Ford-On_the_Lam_from_the_Furniture_Police-slides.pdf"&gt;On the Lam from the Furniture Police&lt;/a&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;presentation.  There he explained how the right and left sides of your brain operate and how they contend for time.  The right side of your brain is where your creative  non-linear thoughts occur while the left side of you brain spends it's time with rational linear thought processes.  It turns out that if you can occupy the left side of your brain with mundane white noise like sleeping, driving, showering, WoW, the wife, whatever...you free up time for your right brain to produce meaningful analytical abstractions without your left brain interfering.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_T5piBwJWgOI/Sc19pBKsiHI/AAAAAAAAACA/CZkqQEKPoNU/s1600-h/giantduck2.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 200px; height: 200px;" src="http://4.bp.blogspot.com/_T5piBwJWgOI/Sc19pBKsiHI/AAAAAAAAACA/CZkqQEKPoNU/s200/giantduck2.jpg" alt="" id="BLOGGER_PHOTO_ID_5318044878635829362" border="0" /&gt;&lt;/a&gt;How many times have you resolved an issue while you are in the process of explaining it to a coworker.  Your own blabbering can shut down your right brain so your left brain can reason things out!  Here is another cool technique that Neal Ford suggested which sounded bizarre at first to me but then made good sense (to me at least).  He suggested putting a rubber duck on top of your monitor and when you get stuck you explain the issue to the duck.  This is not a magic duck (I don't think).  The point is you are freeing up your right brain to perform the tasks you are really interested in.&lt;br /&gt;&lt;br /&gt;I am pretty interested in understanding more about how the brain functions and will probably read Andy Hunt's book &lt;a href="http://www.pragprog.com/titles/ahptl/pragmatic-thinking-and-learning"&gt;Pragmatic Thinking and Learning: Refactor Your Wetware&lt;/a&gt;.  Maybe if I can control my mind I might be able to control my code.  Those of you that know me might agree...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-337792172352205090?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/337792172352205090/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=337792172352205090' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/337792172352205090'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/337792172352205090'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2009/03/i-code-better-without-computer.html' title='I Code Better Without A Computer'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_T5piBwJWgOI/Sc19pBKsiHI/AAAAAAAAACA/CZkqQEKPoNU/s72-c/giantduck2.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-1063432663146690378</id><published>2009-03-27T08:58:00.000-07:00</published><updated>2009-03-27T05:58:16.000-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><title type='text'>Swiz is the Shiz</title><content type='html'>Now that I am doing more Flex development I am back to researching MVC frameworks for the Flex client.  I initially did what most Flex developers do as they start learning and create inline code that cannot be maintained.  From there I went to the other side of the spectrum and tried &lt;a href="http://opensource.adobe.com/wiki/display/cairngorm/Cairngorm"&gt;Cairngorm&lt;/a&gt; which did exactly what it said it would do but at the price of being very heavy but keep your code organized.  The biggest problem with Cairngorm is that it lead to code explosion with all of the objects it required you to create.  Needless to say I wanted to find something simpler.&lt;br /&gt;&lt;br /&gt;I then researched some other available frameworks like &lt;a href="http://puremvc.org/"&gt;PureMVC&lt;/a&gt; and &lt;a href="http://mate.asfusion.com/"&gt;Mate&lt;/a&gt;.  PureMVC looks like and octopus of classes and seems like a Cairngorm like framework with incremental improvements.  Mate was a lot closer to what I wanted but seemed to introduce too much custom framework.&lt;br /&gt;&lt;br /&gt;Finally, I found Chris Scott's&lt;a href="http://code.google.com/p/swizframework/"&gt; Swiz framework&lt;/a&gt;.  Swiz works that way I think since I do so much Spring development in Java.  Swiz's main goal is reduce the clutter you maintain in Flex like singletons and service locators by using dependency injection of your components in your application.  Swiz also comes with a very intuitive annotation model for dynamically dispatching and registering event listeners to your methods.  All of the nonsense you would normally write with some of the other frameworks are abstracted in Swiz and you end up with little framework noise.  Visit the Swiz site and look at the docs.  You will be up and running in about an hour if you understand how dependency injection works already.  You can also check out &lt;a href="http://coenraets.org/blog/2009/02/sample-application-using-the-swiz-framework-and-blazeds/"&gt;Christope Coenraets Swiz&lt;/a&gt; example for a simple running example.  Also look at the &lt;a href="http://link.brightcove.com/services/player/bcpid1733261879?bclid=1729365228&amp;amp;bctid=1738803894"&gt;Flex 360 video&lt;/a&gt; from Chris Scott for a full explaination about this framework.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-1063432663146690378?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/1063432663146690378/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=1063432663146690378' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/1063432663146690378'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/1063432663146690378'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2009/02/swiz-is-shiz.html' title='Swiz is the Shiz'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-8735844489362319051</id><published>2009-02-24T09:17:00.000-08:00</published><updated>2009-02-24T09:54:30.574-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Cool Tools'/><title type='text'>Mockups</title><content type='html'>I found this cool little mockup tool that is very easy to work with called &lt;a href="http://www.balsamiq.com/products/mockups"&gt;Balsamiq&lt;/a&gt;.  I generally use OmniGraffle for sketching mockups and wire frames but I found this tool to be very enjoyable to work with.  When you DnD you elements to the screen you get lots of options for editing that is very intuitive.  Here is a quick example of something I whipped up with this tool so you can see what you can do in 2 minutes.  The images are cartoonish but I think it will resonate well with end-users when doing design meetings or technical presentations.  Simple! Easy to use! Great tool!&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_T5piBwJWgOI/SaQsTOoc-zI/AAAAAAAAABw/lQ3uec9Aoq0/s1600-h/Picture+2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 400px; height: 303px;" src="http://3.bp.blogspot.com/_T5piBwJWgOI/SaQsTOoc-zI/AAAAAAAAABw/lQ3uec9Aoq0/s400/Picture+2.png" alt="" id="BLOGGER_PHOTO_ID_5306414969806191410" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-8735844489362319051?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/8735844489362319051/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=8735844489362319051' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/8735844489362319051'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/8735844489362319051'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2009/02/mockups.html' title='Mockups'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_T5piBwJWgOI/SaQsTOoc-zI/AAAAAAAAABw/lQ3uec9Aoq0/s72-c/Picture+2.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-8605562372790950938</id><published>2009-02-16T16:22:00.000-08:00</published><updated>2009-02-16T16:36:16.961-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CI'/><title type='text'>Hudson, run a bypass</title><content type='html'>I thought I would share a pleasant experience I had with a CI (continuous integration) tool called Hudson.  I have used other tools like this in the past including Anthill, CruiseControl, and TeamCity.  While these tools did their job none of them seemed as easy to use as Hudson.&lt;br /&gt;&lt;br /&gt;Hudson downloads as a self contained war file that launches from a command line:&lt;br /&gt;&lt;br /&gt;java -jar hudson.war&lt;br /&gt;&lt;br /&gt;That is really it to get running and there is nothing else to install.  Hudson comes with a built in servlet engine called Winstone.  There are no configuration files that you have to touch because you use the built-in intuitive UI to configure the Hudson server and your projects.  There are tons of plugins available that can be used to extend the core functionality or you can write your own custom plugins.  We run Starteam (yuck) for source control internally but I found a plugin for it and installed it from the web administration.  Very simple!  I was able to configure SCM checkout, setup email notifications, and execute our build script successfully in about 15 minutes without knowing the product.  If you want to see what awesome open source projects look like check out Hudson.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-8605562372790950938?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/8605562372790950938/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=8605562372790950938' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/8605562372790950938'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/8605562372790950938'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2009/02/hudson-run-bypass.html' title='Hudson, run a bypass'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-5184033033504448140</id><published>2009-02-01T07:28:00.000-08:00</published><updated>2009-02-24T09:53:56.412-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Flex'/><title type='text'>Flex 3 Builder on Mac OSX</title><content type='html'>I ran into problems installing the Flex 3 Builder Professional edition on my Mac.  This was because my current version of Java was set to 1.6.0.  Before you install make sure you go in your Java Preferences (from spotlight) and make sure your Java application setting is J2SE 5.0 32 Bit by dragging it to the top of the list (temporarily).  Then install Flex Builder 3 from the DMG file.  You will not encounter any errors in the installation.  &lt;br /&gt;&lt;br /&gt;Finally, I needed to use the latest version of Java but Flex Builder still needs to run on the 1.5 JDK.  For this I went through Finder to edit&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;span style="color:blue"&gt;/Applications/Adobe Flex Builder3/Flex Builder.app/Contents/MacOS/FlexBuilder.ini&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;From here just add these lines to the top of this file so you can use the right version of Java:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&lt;span style="font-weight:bold;color:blue"&gt;-vm&lt;br /&gt;/System/Library/Frameworks/JavaVM.framework/Versions/1.5.0&lt;/span&gt;&lt;br /&gt;-vmargs&lt;br /&gt;-Xdock:icon=../Resources/flexbuilder.icns&lt;br /&gt;-Xdock:name=Flex Builder&lt;br /&gt;-XstartOnFirstThread&lt;br /&gt;-Xms128m&lt;br /&gt;-Xmx512m&lt;br /&gt;-XX:MaxPermSize=256m&lt;br /&gt;-XX:PermSize=64m&lt;br /&gt;-Dorg.eclipse.swt.internal.carbon.smallFonts&lt;br /&gt;-Dorg.eclipse.swt.internal.carbon.noFocusRing&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Now when Flex Builder starts it will use the right version of Java.  Go back into Java Preferences and move Java SE 6 back to the top of the list if that is what you want.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-5184033033504448140?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/5184033033504448140/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=5184033033504448140' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/5184033033504448140'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/5184033033504448140'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2009/02/flex-3-builder-on-mac-osx.html' title='Flex 3 Builder on Mac OSX'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-6204487445692636554</id><published>2009-01-20T14:46:00.000-08:00</published><updated>2009-01-21T12:01:11.420-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Grails'/><title type='text'>Grails Pitfalls and Tips</title><content type='html'>I have worked with Grails for some time now and thought I would share some of the common mistakes I have made early on.  I also thought I could discuss some things that can make developing with Grails easier.  Here is my short list of items:&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Zero Turnaround Time&lt;/h3&gt;One of the convenient features of Grails is the ability to make a change and have that change be available immediately in your application.  This is an obvious time saver and allows you to be more productive while developing your application.  However, there are times when you will make major refactoring changes on the fly that the JVM cannot deal with.  An example of this might be when you introduce something new in the middle of an existing class hierarchy.  While it may appear that Grails is happy and your environment is sailing along you might see odd behaviour in the application and see some strange runtime exceptions along the way.  What usually remedies this situation is instructing Grails to rebuild the internal structures in your application.  This can be done by executing the following command and restarting your application:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;# grails clean&lt;br /&gt;&lt;/pre&gt;&lt;h3&gt;Grails Console&lt;/h3&gt;One tool that you will want to keep in your Grails toolkit is the Grails console.  This allows you to run a your application in an enviroment so you can experiment with code on the fly.  One of the main reasons I do this is to try out my unit and integration code in the console before, or during, writing my tests.  This is a fast way to get confidence that your test code will provide the results you want.  And because you are doing this in the console you do not have to keep executing the grails test-app command to reinitialize the environment.&lt;br /&gt;&lt;br /&gt;Since you have the full environment in the Grails console you can easily get references to your Spring beans and services.  You are automatically provided a reference to the org.codehaus.groovy.grails.commons.spring.GrailsWebApplicationContext class with a variable named ctx.  This allows you to do things like this in the Grails console:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;  def service = ctx.bookStoreService&lt;br /&gt;  def book = service.getCurrentBook()&lt;br /&gt;  println "The most recent book is ${book.name} by ${book.author}"&lt;br /&gt;  println book.dump()&lt;br /&gt;&lt;/pre&gt; You can see all of the defined beans that Grails is aware of like this:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;ctx.beanDefinitionNames.sort().each {println it}&lt;br /&gt;&lt;/pre&gt;&lt;h3&gt;Manually Assigning Primary Keys&lt;/h3&gt;Most of the time your domain objects will have autogenerated primary key values for the id field.  However, I ran into a situation where I needed to manually assign a primary keys for one of my domain objects.  This took a little bit of trial and error to make this work.  Here is how you do this correctly:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;          &lt;br /&gt;String id&lt;br /&gt;static mapping = {&lt;br /&gt;  id generator:'assigned', column:'RECORD_ID', type:'string'&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Also note that you must set the id manually outside of the object constructor on a separate line like this:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;def obj = new MyDomainObject(name:'whatever')&lt;br /&gt;obj.id = 'REC000001'&lt;br /&gt;obj.save(flush:true)&lt;br /&gt;&lt;/pre&gt;        &lt;br /&gt;Not like this:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;def obj = new MyDomainObjects(name:'whatever', id:'REC000001')&lt;br /&gt;obj.save(flush:true)  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;because the value will not be saved.&lt;br/&gt;&lt;br /&gt;&lt;h3&gt;Manually Triggering Quartz Jobs in Grails&lt;/h3&gt;Grails has an excellent plugin for managing scheduled Quartz jobs.  However, I had a situation where I wanted to manually schedule a job.  I could not find this in the documentation so after poking around in the source I figured out how to do this using the following technique.  First you have to define the job name and group like this:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;class MyValidationJob {&lt;br /&gt;      &lt;br /&gt;  def name = 'MyJobName'&lt;br /&gt;  def group = 'MyGroup'&lt;br /&gt;&lt;br /&gt;  // your executable job method here...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then you can manually trigger the job like this:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;          // trigger the job manually by job name and group from your controller/service/whatever&lt;br /&gt;        def quartzScheduler // inject this in your class&lt;br /&gt;        quartzScheduler.triggerJob("MyJobName", "MyGroup")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h3&gt;Using Packages in the Grails&lt;/h3&gt;Grails provides a directory structure for your application components by convention similar to Rails.  However, this can cause issues if you put your classes directly inside of these folders because they seen by Java in the default package.  Using the default package can cause you issues when compiling your classes.  Since we are using Groovy (and really Java) it is my opinion that you should add packages to your Groovy classes.  I also think that modules in Ruby translate better than package structures at this point but we are stuck with Java constraints.&lt;br /&gt;&lt;br /&gt;You can add packages to the provided services and domain folders and Grails will make Spring beans out of them.  You just need to be careful not to use the same name for the bean twice.  You just reference the bean by the name of the class.  For example let's say you have a service called com.myco.service.validation.MyValidationService.  Grails will expose this as a bean reference named myValidationService.&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;&lt;h3&gt;Base Classes with GORM&lt;/h3&gt;It is common practice that developers make a base domain class with common features of your subclassed domain objects.  By defining an abstract class you can define common properties for your domain objects.  There is a catch however.  The GORM DSL allows you to define constraints and mappings blocks in these classes for validation purposes and defining ORM mappings respectively.  The current problem is that if your subclasses have their own mappings and/or constraints defined they will override what is in your superclass instead of being merged with the superclass blocks.  I am hoping that a future version of Grails addresses this issue (1.1 hopefully).&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;&lt;h3&gt;Unexpected NPEs&lt;/h3&gt;One of the earliest mistakes I made when working with Grails was generating NullPointerExceptions without much to decipher in the stack trace.  The most common problem for me (which you will overcome quickly) was to make sure you validate your domain objects and handle the errors appropriately in your controllers.  The errors object is automatically added to your domain objects and can be checked with the hasErrors method on those objects.&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;&lt;h3&gt;Viewing the Source of your GSP Files&lt;/h3&gt;This is just a quick tip that allows you to view the generated source of you GSP files.  This is a simple as adding the 'showSource' parameter to your URL like this:&lt;br /&gt;&lt;pre name="code" class="jscript"&gt;&lt;br /&gt;http://localhost/yourapp/yourController/list?sort=dateCreated&amp;amp;max=20&amp;amp;showSource&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-6204487445692636554?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/6204487445692636554/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=6204487445692636554' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/6204487445692636554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/6204487445692636554'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2009/01/grails-pitfalls-and-tips.html' title='Grails Pitfalls and Tips'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-9221441781860640640</id><published>2008-09-08T03:15:00.000-07:00</published><updated>2008-09-11T11:26:24.883-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Grails'/><title type='text'>Generating GORM Domain Classes</title><content type='html'>I am on a project that already had a legacy database in place with about 100 tables.  I did not want to create my GORM domain classes manually for my Grails project.  I am surprised  that I did not find any existing code to handle this code generation so I created a Groovy script to handle this.  I am sure this code could use some improvements such as handling more SQL types so I will just call it a 0.1 release because it does what I needed for now.  Modify the &lt;code&gt;sql&lt;/code&gt; varible so it has the right database connection information.  Also, modify the &lt;code&gt;tables&lt;/code&gt; hash so you can control which domain classes to generate based on a [tableName:className] structure.&lt;br /&gt;&lt;br /&gt;&lt;pre class="blackboard"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; &lt;span class="Keyword"&gt;import&lt;/span&gt; &lt;span class="Entity"&gt;groovy.sql.Sql&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   2 &lt;/span&gt; &lt;span class="Keyword"&gt;import&lt;/span&gt; &lt;span class="Entity"&gt;java.sql.Types&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   3 &lt;/span&gt; &lt;br /&gt;&lt;span class="line-numbers"&gt;   4 &lt;/span&gt; sql = &lt;span class="Storage"&gt;Sql&lt;/span&gt;.newInstance(&lt;span class="String"&gt;&amp;quot;jdbc:mysql://localhost:3306/pts_development&amp;quot;&lt;/span&gt;,&lt;br /&gt;&lt;span class="line-numbers"&gt;   5 &lt;/span&gt;                      &lt;span class="String"&gt;&amp;quot;root&amp;quot;&lt;/span&gt;,&lt;br /&gt;&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;                      &lt;span class="String"&gt;&amp;quot;password&amp;quot;&lt;/span&gt;,&lt;br /&gt;&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;                      &lt;span class="String"&gt;&amp;quot;com.mysql.jdbc.Driver&amp;quot;&lt;/span&gt;)&lt;br /&gt;&lt;span class="line-numbers"&gt;   8 &lt;/span&gt; &lt;br /&gt;&lt;span class="line-numbers"&gt;   9 &lt;/span&gt; &lt;span class="Comment"&gt;// tables hash format =&amp;gt; table : className&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  10 &lt;/span&gt; tables = [&lt;span class="String"&gt;'PTS_REG_DTL'&lt;/span&gt;:&lt;span class="String"&gt;'Detail'&lt;/span&gt;,&lt;span class="String"&gt;'PTS_REG_MST'&lt;/span&gt;:&lt;span class="String"&gt;'Master'&lt;/span&gt;]&lt;br /&gt;&lt;span class="line-numbers"&gt;  11 &lt;/span&gt; tables.each { table, className &lt;span class="Keyword"&gt;-&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  12 &lt;/span&gt;     &lt;span class="Support"&gt;File&lt;/span&gt; file = &lt;span class="Keyword"&gt;new&lt;/span&gt; &lt;span class="Support"&gt;File&lt;/span&gt;(&lt;span class="String"&gt;&amp;quot;${className}.groovy&amp;quot;&lt;/span&gt;)&lt;br /&gt;&lt;span class="line-numbers"&gt;  13 &lt;/span&gt;     sb = &lt;span class="Keyword"&gt;new&lt;/span&gt; &lt;span class="Support"&gt;StringBuilder&lt;/span&gt;()&lt;br /&gt;&lt;span class="line-numbers"&gt;  14 &lt;/span&gt; &lt;br /&gt;&lt;span class="line-numbers"&gt;  15 &lt;/span&gt;     fieldNames = []&lt;br /&gt;&lt;span class="line-numbers"&gt;  16 &lt;/span&gt;     fieldTypes = []&lt;br /&gt;&lt;span class="line-numbers"&gt;  17 &lt;/span&gt; &lt;br /&gt;&lt;span class="line-numbers"&gt;  18 &lt;/span&gt;     query = &lt;span class="String"&gt;&amp;quot;select * from ${table}&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  19 &lt;/span&gt;     sql.query(query.toString()){ rs &lt;span class="Keyword"&gt;-&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  20 &lt;/span&gt; &lt;br /&gt;&lt;span class="line-numbers"&gt;  21 &lt;/span&gt;         sb &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; &lt;span class="String"&gt;&amp;quot;class ${className} {&lt;span class="Constant"&gt;\n&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  22 &lt;/span&gt; &lt;br /&gt;&lt;span class="line-numbers"&gt;  23 &lt;/span&gt;         &lt;span class="Storage"&gt;def&lt;/span&gt; meta = rs.metaData&lt;br /&gt;&lt;span class="line-numbers"&gt;  24 &lt;/span&gt;         &lt;span class="Keyword"&gt;if&lt;/span&gt;(meta.columnCount &lt;span class="Keyword"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="Constant"&gt;0&lt;/span&gt;) &lt;span class="Keyword"&gt;return&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  25 &lt;/span&gt; &lt;br /&gt;&lt;span class="line-numbers"&gt;  26 &lt;/span&gt;         &lt;span class="Keyword"&gt;for&lt;/span&gt;(i in &lt;span class="Constant"&gt;1&lt;/span&gt;..meta.columnCount){&lt;br /&gt;&lt;span class="line-numbers"&gt;  27 &lt;/span&gt; &lt;br /&gt;&lt;span class="line-numbers"&gt;  28 &lt;/span&gt;             fieldName = meta.getColumnName(i).toLowerCase().replaceAll(&lt;span class="Keyword"&gt;/&lt;/span&gt;_[\w]&lt;span class="Keyword"&gt;/&lt;/span&gt;,{ it[&lt;span class="Constant"&gt;1&lt;/span&gt;].toUpperCase()})&lt;br /&gt;&lt;span class="line-numbers"&gt;  29 &lt;/span&gt;             fieldNames &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; fieldName&lt;br /&gt;&lt;span class="line-numbers"&gt;  30 &lt;/span&gt;             fieldType = &lt;span class="String"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  31 &lt;/span&gt; &lt;br /&gt;&lt;span class="line-numbers"&gt;  32 &lt;/span&gt;             &lt;span class="Keyword"&gt;switch&lt;/span&gt;(meta.getColumnType(i)) {&lt;br /&gt;&lt;span class="line-numbers"&gt;  33 &lt;/span&gt;                 &lt;span class="Keyword"&gt;case&lt;/span&gt; &lt;span class="Support"&gt;Types&lt;/span&gt;.&lt;span class="Constant"&gt;BIGINT&lt;/span&gt;:&lt;br /&gt;&lt;span class="line-numbers"&gt;  34 &lt;/span&gt;                     fieldType = &lt;span class="String"&gt;&amp;quot;Long&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  35 &lt;/span&gt;                     &lt;span class="Keyword"&gt;break&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  36 &lt;/span&gt;                 &lt;span class="Keyword"&gt;case&lt;/span&gt; [&lt;span class="Support"&gt;Types&lt;/span&gt;.&lt;span class="Constant"&gt;BINARY&lt;/span&gt;, &lt;span class="Support"&gt;Types&lt;/span&gt;.&lt;span class="Constant"&gt;BIT&lt;/span&gt;, &lt;span class="Support"&gt;Types&lt;/span&gt;.&lt;span class="Constant"&gt;BOOLEAN&lt;/span&gt;, &lt;span class="Support"&gt;Types&lt;/span&gt;.&lt;span class="Constant"&gt;SMALLINT&lt;/span&gt;]:&lt;br /&gt;&lt;span class="line-numbers"&gt;  37 &lt;/span&gt;                     fieldType = &lt;span class="String"&gt;&amp;quot;Boolean&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  38 &lt;/span&gt;                     &lt;span class="Keyword"&gt;break&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  39 &lt;/span&gt;                 &lt;span class="Keyword"&gt;case&lt;/span&gt; [&lt;span class="Support"&gt;Types&lt;/span&gt;.&lt;span class="Constant"&gt;CHAR&lt;/span&gt;, &lt;span class="Support"&gt;Types&lt;/span&gt;.&lt;span class="Constant"&gt;VARCHAR&lt;/span&gt;]:&lt;br /&gt;&lt;span class="line-numbers"&gt;  40 &lt;/span&gt;                     fieldType = &lt;span class="String"&gt;&amp;quot;String&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  41 &lt;/span&gt;                     &lt;span class="Keyword"&gt;break&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  42 &lt;/span&gt;                 &lt;span class="Keyword"&gt;case&lt;/span&gt; [&lt;span class="Support"&gt;Types&lt;/span&gt;.&lt;span class="Constant"&gt;DATE&lt;/span&gt;, &lt;span class="Support"&gt;Types&lt;/span&gt;.&lt;span class="Constant"&gt;TIMESTAMP&lt;/span&gt;]:&lt;br /&gt;&lt;span class="line-numbers"&gt;  43 &lt;/span&gt;                     fieldType = &lt;span class="String"&gt;&amp;quot;Date&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  44 &lt;/span&gt;                     &lt;span class="Keyword"&gt;break&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  45 &lt;/span&gt;                 &lt;span class="Keyword"&gt;case&lt;/span&gt; [&lt;span class="Support"&gt;Types&lt;/span&gt;.&lt;span class="Constant"&gt;DECIMAL&lt;/span&gt;, &lt;span class="Support"&gt;Types&lt;/span&gt;.&lt;span class="Constant"&gt;FLOAT&lt;/span&gt;]:&lt;br /&gt;&lt;span class="line-numbers"&gt;  46 &lt;/span&gt;                     fieldType = &lt;span class="String"&gt;&amp;quot;Decimal&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  47 &lt;/span&gt;                     &lt;span class="Keyword"&gt;break&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  48 &lt;/span&gt;                 &lt;span class="Keyword"&gt;case&lt;/span&gt; [&lt;span class="Support"&gt;Types&lt;/span&gt;.&lt;span class="Constant"&gt;INTEGER&lt;/span&gt;, &lt;span class="Support"&gt;Types&lt;/span&gt;.&lt;span class="Constant"&gt;NUMERIC&lt;/span&gt;]:&lt;br /&gt;&lt;span class="line-numbers"&gt;  49 &lt;/span&gt;                     fieldType = &lt;span class="String"&gt;&amp;quot;Integer&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  50 &lt;/span&gt;                     &lt;span class="Keyword"&gt;break&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  51 &lt;/span&gt;                 &lt;span class="Keyword"&gt;default&lt;/span&gt;:&lt;br /&gt;&lt;span class="line-numbers"&gt;  52 &lt;/span&gt;                     fieldType = &lt;span class="String"&gt;&amp;quot;Object&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  53 &lt;/span&gt;             }&lt;br /&gt;&lt;span class="line-numbers"&gt;  54 &lt;/span&gt;             fieldTypes &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; fieldType&lt;br /&gt;&lt;span class="line-numbers"&gt;  55 &lt;/span&gt;             sb &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; &lt;span class="String"&gt;&amp;quot;&lt;span class="Constant"&gt;\t&lt;/span&gt;${fieldType} ${fieldName}&lt;span class="Constant"&gt;\n&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  56 &lt;/span&gt;         }&lt;br /&gt;&lt;span class="line-numbers"&gt;  57 &lt;/span&gt; &lt;br /&gt;&lt;span class="line-numbers"&gt;  58 &lt;/span&gt;         &lt;span class="Comment"&gt;// Print out the table mappings&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  59 &lt;/span&gt;         sb &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; &lt;span class="String"&gt;&amp;quot;&lt;span class="Constant"&gt;\t&lt;/span&gt;static mapping = {&lt;span class="Constant"&gt;\n&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  60 &lt;/span&gt;         sb &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; &lt;span class="String"&gt;&amp;quot;&lt;span class="Constant"&gt;\t&lt;/span&gt;&lt;span class="Constant"&gt;\t&lt;/span&gt;table '${table.toString()}'&lt;span class="Constant"&gt;\n&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  61 &lt;/span&gt; &lt;br /&gt;&lt;span class="line-numbers"&gt;  62 &lt;/span&gt;         &lt;span class="Keyword"&gt;for&lt;/span&gt;(j in &lt;span class="Constant"&gt;1&lt;/span&gt;..meta.columnCount) {&lt;br /&gt;&lt;span class="line-numbers"&gt;  63 &lt;/span&gt;             sb &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; &lt;span class="String"&gt;&amp;quot;&lt;span class="Constant"&gt;\t&lt;/span&gt;&lt;span class="Constant"&gt;\t&lt;/span&gt;${fieldNames[j-1]}  column:'${meta.getColumnName(j)}'&lt;span class="Constant"&gt;\n&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  64 &lt;/span&gt;         }&lt;br /&gt;&lt;span class="line-numbers"&gt;  65 &lt;/span&gt;         sb &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; &lt;span class="String"&gt;&amp;quot;&lt;span class="Constant"&gt;\t&lt;/span&gt;}&lt;span class="Constant"&gt;\n&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  66 &lt;/span&gt; &lt;br /&gt;&lt;span class="line-numbers"&gt;  67 &lt;/span&gt;         &lt;span class="Comment"&gt;// Print out the table constraints&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  68 &lt;/span&gt;         sb &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; &lt;span class="String"&gt;&amp;quot;&lt;span class="Constant"&gt;\t&lt;/span&gt;static constraints = {&lt;span class="Constant"&gt;\n&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  69 &lt;/span&gt; &lt;br /&gt;&lt;span class="line-numbers"&gt;  70 &lt;/span&gt;         &lt;span class="Keyword"&gt;for&lt;/span&gt;(k in &lt;span class="Constant"&gt;1&lt;/span&gt;..meta.columnCount) {&lt;br /&gt;&lt;span class="line-numbers"&gt;  71 &lt;/span&gt;             sb &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; &lt;span class="String"&gt;&amp;quot;&lt;span class="Constant"&gt;\t&lt;/span&gt;&lt;span class="Constant"&gt;\t&lt;/span&gt;${fieldNames[k-1]}(&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  72 &lt;/span&gt;             sb &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; &lt;span class="String"&gt;&amp;quot;nullable:${meta.isNullable(k) ? 'true': 'false'}&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  73 &lt;/span&gt; &lt;br /&gt;&lt;span class="line-numbers"&gt;  74 &lt;/span&gt;             &lt;span class="Keyword"&gt;if&lt;/span&gt;(fieldTypes[k] &lt;span class="Keyword"&gt;==&lt;/span&gt; &lt;span class="String"&gt;'String'&lt;/span&gt;) {&lt;br /&gt;&lt;span class="line-numbers"&gt;  75 &lt;/span&gt;                 sb &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; &lt;span class="String"&gt;&amp;quot;, maxSize:${meta.getColumnDisplaySize(k)}&amp;quot;&lt;/span&gt;    &lt;br /&gt;&lt;span class="line-numbers"&gt;  76 &lt;/span&gt;             }&lt;br /&gt;&lt;span class="line-numbers"&gt;  77 &lt;/span&gt;             sb &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; &lt;span class="String"&gt;&amp;quot;)&lt;span class="Constant"&gt;\n&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  78 &lt;/span&gt;         }&lt;br /&gt;&lt;span class="line-numbers"&gt;  79 &lt;/span&gt;         sb &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; &lt;span class="String"&gt;&amp;quot;&lt;span class="Constant"&gt;\t&lt;/span&gt; }&lt;span class="Constant"&gt;\n&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  80 &lt;/span&gt;         sb &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt; &lt;span class="String"&gt;&amp;quot;}&lt;span class="Constant"&gt;\n&lt;/span&gt;&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  81 &lt;/span&gt;     }&lt;br /&gt;&lt;span class="line-numbers"&gt;  82 &lt;/span&gt;     file.write(sb.toString())&lt;br /&gt;&lt;span class="line-numbers"&gt;  83 &lt;/span&gt; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This code will generate a separate file for each domain class you specified in the tables hash in the current directory.  Just make sure you have your database jar file in your classpath.  Here is how you could run this code from the command line:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;groovy -cp .:/Users/meagle/java/api/mysql-connector-java-5.0.4/mysql-connector-java-5.0.4-bin.jar GormGenerator.groovy&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-9221441781860640640?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/9221441781860640640/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=9221441781860640640' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/9221441781860640640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/9221441781860640640'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2008/09/generating-gorm-domain-classes.html' title='Generating GORM Domain Classes'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-3646561697283004212</id><published>2008-08-15T10:28:00.000-07:00</published><updated>2008-08-15T10:55:28.145-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Crowd'/><category scheme='http://www.blogger.com/atom/ns#' term='Grails'/><title type='text'>Securing a Grails application with Acegi and Crowd (Revisited)</title><content type='html'>I recently had a need to combine these technologies to provide authentication and authorization with Crowd in my Grails application. I ran across a very good article that handholds you through this process. You can reference the original aricle &lt;a href="http://blogs.atlassian.com/developer/2008/03/threes_a_crowd_securing_a_grai.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;However, I ran into several issue when I tried to apply the configuration in the article. The original article uses Grails 1.0.1, the Acegi Security Plugin v0.2, and Crowd 1.3. When I attempted to follow the instructions I attempted to use Grails 1.0.3, the Acegi Security Plugin v0.3, and Crowd 1.4. If you are still reading then make sure that you continue to use 0.2.1 version of the plugin because the Crowd integration module has not been updated to reference the latest Acegi/Spring Security framework updates such as pack naming. From here I will reference where you will need to make modifications for each step that requires it in the original article.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Step 1: Grails &amp;amp; Acegi Installation&lt;/h3&gt;Again, use the Acegi Security Plugin v0.2.1&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Step 2: Create Grails Application with Acegi Security&lt;/h3&gt;Download and install the &lt;a href="http://plugins.grails.org/grails-acegi/tags/RELEASE_0_2_1/grails-acegi-0.2.1.zip"&gt;Acegi Security Plugin v0.2.1&lt;/a&gt; in your Grails project.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Step 3: Crowd Download&lt;/h3&gt;Download &lt;a hef="http://www.atlassian.com/software/crowd/CrowdDownloadCenter.jspa"&gt;Crowd v.1.4.4&lt;/a&gt; from the Atlassian website.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Step 4: Crowd Installation &amp;amp; Configuration&lt;/h3&gt;The installation and configuration is surprisingly as easy as the original author suggests.  Just follow the &lt;a href="http://confluence.atlassian.com/display/CROWD/Installing+Crowd"&gt;installation instructions&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Step 5: Grails + Acegi + Crowd Integration&lt;/h3&gt;Make absolutely sure that you do not skip the step regarding crowd.properties. If the application name and password do not match the same values in the application configuration you will not be allowed to attempt authentication against Crowd. You can setup these values in the Crowd administration console by navigating to the Applications tab and selecting the application from the list. From there click the Details tab and ensure the name and password are set to the same values as crowd.properties. Also, make sure that the application.login.url value is modified to the correct login url. The one specified in the article should be consistent with the example.&lt;br /&gt;&lt;br /&gt;I did follow the author's lead and copied the applicationContext-CrowdClient.xml file from the&lt;br /&gt;CROWD_HOME/client/crowd-integration-client-1.4.4.jar file and positioned it in the ACEGI_APP/conf/spring/ directory and then renamed the file to resources.xml.&lt;br /&gt;&lt;br /&gt;The modifications I had to make to resources.xml were different from the author's article.  Here are the beans I added:&lt;br /&gt;&lt;br /&gt;&lt;pre class="blackboard"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt; &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;bean id=&lt;span class="String"&gt;"crowdUserDetailsService"&lt;/span&gt; &lt;span class="Storage"&gt;class&lt;/span&gt;=&lt;span class="String"&gt;"com.atlassian.crowd.integration.acegi.user.CrowdUserDetailsServiceImpl"&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   2 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;property name=&lt;span class="String"&gt;"authenticationManager"&lt;/span&gt; ref=&lt;span class="String"&gt;"crowdAuthenticationManager"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   3 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;property name=&lt;span class="String"&gt;"groupMembershipManager"&lt;/span&gt; ref=&lt;span class="String"&gt;"crowdGroupMembershipManager"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   4 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;property name=&lt;span class="String"&gt;"userManager"&lt;/span&gt; ref=&lt;span class="String"&gt;"crowdUserManager"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   5 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;!&lt;/span&gt;&lt;span class="Keyword"&gt;--&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;property name=&lt;span class="String"&gt;"authorityPrefix"&lt;/span&gt; value=&lt;span class="String"&gt;"ROLE_"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;span class="Keyword"&gt;--&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;property name=&lt;span class="String"&gt;"authorityPrefix"&lt;/span&gt; value=&lt;span class="String"&gt;""&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;     &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;bean&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   8 &lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;     &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;bean id=&lt;span class="String"&gt;"crowdAuthenticationProvider"&lt;/span&gt; &lt;span class="Storage"&gt;class&lt;/span&gt;=&lt;span class="String"&gt;"com.atlassian.crowd.integration.acegi.CrowdAuthenticationProvider"&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  10 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;property name=&lt;span class="String"&gt;"userDetailsService"&lt;/span&gt; ref=&lt;span class="String"&gt;"crowdUserDetailsService"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  11 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;property name=&lt;span class="String"&gt;"authenticationManager"&lt;/span&gt; ref=&lt;span class="String"&gt;"crowdAuthenticationManager"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  12 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;property name=&lt;span class="String"&gt;"httpAuthenticator"&lt;/span&gt; ref=&lt;span class="String"&gt;"httpAuthenticator"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  13 &lt;/span&gt;     &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;bean&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  14 &lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  15 &lt;/span&gt;     &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;bean id=&lt;span class="String"&gt;"crowdLogoutHandler"&lt;/span&gt; &lt;span class="Storage"&gt;class&lt;/span&gt;=&lt;span class="String"&gt;"com.atlassian.crowd.integration.acegi.CrowdLogoutHandler"&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  16 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;property name=&lt;span class="String"&gt;"httpAuthenticator"&lt;/span&gt; ref=&lt;span class="String"&gt;"httpAuthenticator"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  17 &lt;/span&gt;     &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;bean&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  18 &lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  19 &lt;/span&gt;     &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;bean id=&lt;span class="String"&gt;"logoutFilter"&lt;/span&gt; &lt;span class="Storage"&gt;class&lt;/span&gt;=&lt;span class="String"&gt;"org.acegisecurity.ui.logout.LogoutFilter"&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  20 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;constructor&lt;span class="Keyword"&gt;-&lt;/span&gt;arg value=&lt;span class="String"&gt;"/"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  21 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;constructor&lt;span class="Keyword"&gt;-&lt;/span&gt;arg&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  22 &lt;/span&gt;             &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;list&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  23 &lt;/span&gt;                 &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;ref bean=&lt;span class="String"&gt;"crowdLogoutHandler"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  24 &lt;/span&gt;                 &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;bean &lt;span class="Storage"&gt;class&lt;/span&gt;=&lt;span class="String"&gt;"org.acegisecurity.ui.logout.SecurityContextLogoutHandler"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  25 &lt;/span&gt;             &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;list&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  26 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;constructor&lt;span class="Keyword"&gt;-&lt;/span&gt;arg&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  27 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;property name=&lt;span class="String"&gt;"filterProcessesUrl"&lt;/span&gt; value=&lt;span class="String"&gt;"/j_acegi_logout"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  28 &lt;/span&gt;     &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;bean&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  29 &lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  30 &lt;/span&gt;     &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;bean id=&lt;span class="String"&gt;"filterChainProxy"&lt;/span&gt; &lt;span class="Storage"&gt;class&lt;/span&gt;=&lt;span class="String"&gt;"org.acegisecurity.util.FilterChainProxy"&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  31 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;property name=&lt;span class="String"&gt;"filterInvocationDefinitionSource"&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  32 &lt;/span&gt;             &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;value&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  33 &lt;/span&gt;                 &lt;span class="Constant"&gt;CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  34 &lt;/span&gt;                 &lt;span class="Constant"&gt;PATTERN_TYPE_APACHE_ANT&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  35 &lt;/span&gt;                 &lt;span class="Keyword"&gt;/&lt;/span&gt;images&lt;span class="Comment"&gt;/**=#NONE#&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  36 &lt;/span&gt; &lt;span class="Comment"&gt;                /scripts/**=#NONE#&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  37 &lt;/span&gt; &lt;span class="Comment"&gt;                /styles/**=#NONE#&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  38 &lt;/span&gt; &lt;span class="Comment"&gt;                /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  39 &lt;/span&gt; &lt;span class="Comment"&gt;            &amp;lt;/value&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  40 &lt;/span&gt; &lt;span class="Comment"&gt;        &amp;lt;/property&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;  41 &lt;/span&gt; &lt;span class="Comment"&gt;    &amp;lt;/bean&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;h3&gt;AcegiGrailsPlugin.groovy&lt;/h3&gt;&lt;br /&gt;Instead of making this modification to this file I put the change inside of resources.xml. If you make this modificaiton inside the plugin code and you upgrade it you will need to remember this. Instead Grails will override the authenticationManager in AcegiGrailsPlugin with this definition inside of resources.xml:&lt;br /&gt;&lt;br /&gt;&lt;pre class="blackboard"&gt;&lt;span class="line-numbers"&gt;   1 &lt;/span&gt;     &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;bean id=&lt;span class="String"&gt;"authenticationManager"&lt;/span&gt; &lt;span class="Storage"&gt;class&lt;/span&gt;=&lt;span class="String"&gt;"org.acegisecurity.providers.ProviderManager"&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   2 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;property name=&lt;span class="String"&gt;"providers"&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   3 &lt;/span&gt;             &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;list&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   4 &lt;/span&gt;                 &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;ref local=&lt;span class="String"&gt;"crowdAuthenticationProvider"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   5 &lt;/span&gt;                 &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;ref bean=&lt;span class="String"&gt;"anonymousAuthenticationProvider"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   6 &lt;/span&gt;                 &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;ref bean=&lt;span class="String"&gt;"rememberMeAuthenticationProvider"&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   7 &lt;/span&gt;             &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;list&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   8 &lt;/span&gt;         &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;property&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="line-numbers"&gt;   9 &lt;/span&gt;     &lt;span class="Keyword"&gt;&amp;lt;&lt;/span&gt;&lt;span class="Keyword"&gt;/&lt;/span&gt;bean&lt;span class="Keyword"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-3646561697283004212?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/3646561697283004212/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=3646561697283004212' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/3646561697283004212'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/3646561697283004212'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2008/08/securing-grails-application-with-acegi.html' title='Securing a Grails application with Acegi and Crowd (Revisited)'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-3014360313171670707</id><published>2008-04-30T17:21:00.000-07:00</published><updated>2008-06-18T03:46:03.809-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JRuby'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby on Rails'/><title type='text'>Deploying Rails 2.0.2 applications with Tomcat 5.5.x using Warbler</title><content type='html'>After working with Rails for some time I wanted to determine how to deploy an application on Tomcat.  I will discuss how to build a war file that can be used on Tomcat and show pitfalls I experienced along the way.  Here are the technologies I am using for this deployment:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;JRuby 1.1.1&lt;/li&gt;&lt;li&gt;Rails 2.0.2&lt;/li&gt;&lt;li&gt;Warbler 0.9.5&lt;/li&gt;&lt;li&gt;MySQL 5.0.4 &lt;/li&gt;&lt;/ul&gt;To package up my Rails application I am using Warbler as indicated above.  This is an alternative to Goldspike but allows a better packaging scheme and an easier configuration.  Warble is actually a wrapper to Goldspike and includes these dependencies when you install the gem.  You can read more about Warbler &lt;a href="http://blog.nicksieger.com/articles/2007/09/04/warbler-a-little-birdie-to-introduce-your-rails-app-to-java"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I will assume you have Rails installed as a gem in JRuby.  To make a standalone war file make sure you run this command to freeze Rails in your project:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;$ jruby -S rails:freeze:gems&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This will write the standard Rails gem information to your &lt;span style="font-family:courier new;"&gt;RAILS_ROOT/vendor/rails&lt;/span&gt; directory.  Here is how you install Warbler from JRuby:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;$ jruby -S gem install warbler&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;After this is configured switch to the root of your Rails project and execute this command:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;$ jruby -S warble&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This will build an exploded war file in a temporary directory within your project.  This path can be changed in the &lt;span style="font-family:courier new;"&gt;warble.rb&lt;/span&gt; file that gets installed.  Now create a Warble configuration file by executing this command since we will need it (not sure why this is done after the fact):&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;$ jruby -S warble config&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;After generating a configuration file you should consider updating the JRuby jar file that comes with Warble since you will want the latest version packaged with your war file.  If you do not do this Warble will always include the original version of the JRuby jar with your war file.  Add the &lt;span style="font-family:courier new;"&gt;jruby-complete-1.1.1.jar&lt;/span&gt; file which can be obtained &lt;a href="http://repository.codehaus.org/org/jruby/jruby-complete/1.1.1"&gt;here&lt;/a&gt;) to your &lt;span style="font-family:courier new;"&gt;RAILS_HOME/lib&lt;/span&gt; directory.  Grab the latest Goldspike jar file and place it in your &lt;span style="font-family:courier new;"&gt;RAILS_HOME/lib&lt;/span&gt; directory.  Now add this code to the configuration:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;config.java_libs.reject! {&lt;br /&gt; |lib| lib =~ /jruby-complete|goldspike/&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This allows you to externally package your own versions of JRuby and Goldspike and reject the verions of these jar files that come bundled in the gem directory (&lt;span style=";font-family:courier new;font-size:85%;"  &gt;$JRUBY_HOME/lib/ruby/gems/1.8/gems/warbler-0.9.5/lib/warbler&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;Also, you can delete the exploded directory and the war file by issuing this command:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;$ jruby -S warble war:clean&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;From this point you just move the war file to your Tomcat webapps directory and start the server.  This is great when everything works.  However, I was lucky enough to run into some issues while making this happen.&lt;br /&gt;&lt;br /&gt;The first issue occurred when I started Tomcat with the war file I built with Warble (remember that this is using Goldspike under the covers) and got this error message in the Tomcat logs:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-family:courier new;"&gt;“Could not load Rails. See the logs for more details.”&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Yipee!  So when I went to look in the Tomcat logs there was no additional information.  Great!  It turns out that the stack trace output gets swallowed on a Mac.  This is fairly easy to fix (but should be patched by the Goldspike team) by modifying the source code for the goldspike-1.6.1.jar file to emit the full stack trace.  Grab the latest Goldspike code from here:&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;svn checkout&lt;br /&gt;svn://rubyforge.org/var/svn/jruby-extras/trunk/rails-integration&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You will have to modify src/main/java/org/jruby/webapp/RailsFactory.java like this:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;pre&gt;&lt;br /&gt;} catch (RaiseException e) {&lt;br /&gt;&lt;span style="color: rgb(51, 102, 255);"&gt;//added this line so the full stack trace is shown&lt;/span&gt; &lt;span style="color: rgb(51, 102, 255);"&gt;   &lt;br /&gt;e.printStackTrace();&lt;/span&gt;&lt;br /&gt;logRubyException("Failed to load Rails", e);&lt;br /&gt;throw new ServletException(&lt;br /&gt;  "Could not load Rails. See the logs for more details.");&lt;br /&gt;&lt;span style="font-family:Georgia,serif;"&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;/span&gt;After you rebuild the Goldspike jar add it to your RAILS_ROOT/lib.  Now re-warble your war file and put it on your Tomcat server.  Now you should be able to find your error.  In my case I was not making a connection to my database.  I was clearly missing gems in my war file.  Great!&lt;br /&gt;&lt;br /&gt;This really is not too big of a deal.  You can explain to Warbler what gems need to be packaged with your war file.  Here is a snippet from my warble.rb file that shows how I included the missing gems:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;# Gems to be packaged in the webapp.  Note that Rails &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;# &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;gems are added to this &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;list if vendor/rails is not&lt;br /&gt;# &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;present, so be sure to include&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt; rails if you overwrite&lt;br /&gt;# the value&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;config.gems = ["activerecord-jdbc-adapter", "jruby-openssl", "activerecord-jdbcmysql-adapter", "jdbc-mysql"]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Alright re-warble and... still no connectivity.  Awesome!  This time I was missing my MySQL jar file in my war file and the activerecord-jdbcmysql-adapter gem could not communicate with the implementation jar.  To fix this copy your MySQL jar file to the &lt;span style="font-family:courier new;"&gt;RAILS_ROOT/lib&lt;/span&gt; directory in your Rails project.&lt;br /&gt;&lt;br /&gt;One more re-warbling and more good news:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt; &lt;span style="font-family:courier new;"&gt;“Rails Error: No :secret given to the #protect_from_forgery call. Set that or use a session store capable of generating its own keys (Cookie Session Store)”&lt;/span&gt;.  &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is a known bug with Goldspike and Rails 2.0.  So edit your web.xml file and add the following:&lt;span style="font-family:courier new;"&gt;&lt;br /&gt;&lt;pre&gt;&amp;lt;context-param&amp;gt;&lt;br /&gt;&amp;lt;param-name&amp;gt;jruby.session_store&amp;lt;/param-name&amp;gt;&lt;br /&gt;&amp;lt;!-- This value really means let Rails take care &lt;br /&gt;        of session store --&amp;gt;&lt;br /&gt;&amp;lt;param-value&amp;gt;db&amp;lt;/param-value&amp;gt;&lt;br /&gt;&amp;lt;/context-param&amp;gt;&lt;br /&gt;&lt;span style="font-family:Georgia,serif;"&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/span&gt;OK, please just one more re-warbling and deployment to Tomcat and a successful standalone war file has been created and deployed.  *birds singing*&lt;br /&gt;&lt;br /&gt;I would also suggest increasing the Java memory setting in the JAVA_OPTS of Tomcat to at least -Xmx512m to improve performance.  Also keep in mind that Warble is configured to use the production environment for your Rails application so make the appropriate migrations before starting the application.  You can make further modification to the Goldspike configuration within the warble.rb file to tweak the pool of Rails runtimes.  Once I made these changes it was pretty transparent where I was running my Rails application.&lt;br /&gt;&lt;br /&gt;I hope this saves other people time when running into issues.  This was kind of a frustrating process since it should be a simple one line deployment. However, I was happy with the end results.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-3014360313171670707?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/3014360313171670707/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=3014360313171670707' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/3014360313171670707'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/3014360313171670707'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2008/04/deploying-rails-202-applications-with.html' title='Deploying Rails 2.0.2 applications with Tomcat 5.5.x using Warbler'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-7049616130689626946</id><published>2008-01-18T22:20:00.000-08:00</published><updated>2008-01-20T16:42:36.835-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Grails'/><title type='text'>Migrating to Grails?</title><content type='html'>The last time I looked at Grails it was around version 0.4.  Now that we are approaching a 1.0 release I wanted to see how much further the Grails team has advanced.  I wanted to start by using my existing Spring and Hibernate components from my Java project (I am currently using Stripes for our presentation layer).  Benefits for doing this might include:&lt;br /&gt;&lt;ul style="font-size: 0.9em;"&gt;&lt;li&gt;Abstracting your Java domain model and services so they are not coupled to Grails.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The ability to continue using your Java services and model objects outside of Web applications or have the ability to integrate with other Web presentation frameworks.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;A migration path to Groovy if you want to rid yourself of straight Java altogether.  I am sure that there would be massive redux in LOC. For me it was experimenting with the intersection of my experiences with Rails and Java Web development.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Does this mean that if you reuse your Hibernate Java domain objects you do not get all of the GORM features like (order.save())?  No, your domain objects transparently take full advantage of the mixed in methods in Grails.  You might find that the services you are reusing from Spring are mostly passthroughs to DAO that you do not need anymore because of GORM.  Here is a trivial example showing how to use a Grails controller to use existing Spring services to perform DAO operations and how to use your Hibernate domain objects directly as GORM objects:&lt;/p&gt;&lt;br /&gt;&lt;pre&gt;UserService userService  // This is injected in via auto-wiring&lt;br /&gt;&lt;br /&gt;def index = {&lt;br /&gt;&lt;br /&gt;  // Using Java Services&lt;br /&gt;  def user = userService.find(1, User.class)&lt;br /&gt;  println user.firstName&lt;br /&gt;  user.firstName = 'Mark'&lt;br /&gt;  userService.save user&lt;br /&gt;  println User.get(1).firstName&lt;br /&gt;&lt;br /&gt;  // Using strait Grails&lt;br /&gt;  user = User.get(1)&lt;br /&gt;  println user.firstName&lt;br /&gt;  user.firstName = 'Phil'&lt;br /&gt;  user.save()&lt;br /&gt;  println User.get(1).firstName&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Here is how we took our annotated Hiberate classes so Grails would recognize them:&lt;/p&gt;&lt;p&gt;Edit your hibernate.cfg.xml file in the GRAILS_APP/grails_app/conf/hibernate directory and add your domain objects:&lt;/p&gt;&lt;pre&gt;&amp;lt;!DOCTYPE hibernate-configuration SYSTEM&lt;br /&gt;"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"&amp;gt;&lt;br /&gt;  &amp;lt;hibernate-configuration&amp;gt;&lt;br /&gt;    &amp;lt;session-factory&amp;gt;&lt;br /&gt;      &amp;lt;mapping class="com.meagle.common.bo.Address" /&amp;gt;&lt;br /&gt;      &amp;lt;mapping class="com.meagle.common.bo.User" /&amp;gt;&lt;br /&gt;    &amp;lt;/session-factory&amp;gt;&lt;br /&gt;  &amp;lt;/hibernate-configuration&amp;gt;&lt;/pre&gt;In your DataSource.groovy configuration file add the configClass line to your datasource configuration so your annotated Hibernate classes will be recognized by Grails:&lt;pre&gt;dataSource {&lt;br /&gt;  pooled = false&lt;br /&gt;  driverClassName = "com.ibm.db2.jcc.DB2Driver"&lt;br /&gt;  username = "meagle"&lt;br /&gt;  password = "******"&lt;br /&gt;  configClass&lt;br /&gt;   =org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration.class&lt;br /&gt;}&lt;/pre&gt;Here is how you expose your existing Spring beans to your Grails application:&lt;ol style="font-size: 0.9em;"&gt;&lt;li&gt;Edit your GRAILS_APP/grails_app/conf/spring/resources.xml file and add the following line:&lt;br /&gt;&lt;pre&gt;&amp;lt;import resource="applicationContext-core.xml"/&amp;gt;&lt;/pre&gt;This tells Grails to include your Spring resources (We put our Spring config file in the same directory as the resources.xml file).&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Make sure all of your Spring and Hibernate domain objects written in Java are on the classpath.  We put ours in a jar file in the GRAILS_APP/lib directory.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;One gotcha here to remember is that your Spring configuration file should not include any definition to a SessionFactory and your services and DAOs that reference a SessionFactory needs to be the name "sessionFactory".   Otherwise, you will end up with multiple session factory objects which is probably not what you want.&lt;/p&gt;&lt;p&gt;Overall I am very impressed with Grails at this point and would challenge Java developers to take a serious look at this framework. If you are fortunate enough to have IntelliJ 7 then there is decent plugin support for building Grails applications.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-7049616130689626946?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/7049616130689626946/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=7049616130689626946' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/7049616130689626946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/7049616130689626946'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2008/01/migrating-to-grails_19.html' title='Migrating to Grails?'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-5575605749250590836</id><published>2007-12-15T14:24:00.000-08:00</published><updated>2008-12-09T02:25:29.955-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby on Rails'/><title type='text'>NetBeans 6.0 and Rails</title><content type='html'>I have been looking for a good Rails IDE and think I have found one. I was using TextMate which has lots of great quick templating commands for Rails concepts for migrations, rhtml, and navigating between files and methods. However, I enjoy working with IDEs and tried some Eclipse based versions but felt that they were inadequate. My paying job at the time is mostly Java based and I use IntelliJ which makes my development more productive for that type of development.&lt;br /&gt;&lt;br /&gt;I had not looked at NetBeans in some time because most Java developers were using Eclipse and that was fine for me at the time. So when I saw that NetBeans had a Rails related version of their IDE I was willing to give it another look. I can only say that for me this is a very nice IDE that has full functionality around the way I develop with Rails. Here are some of the features I find handy:&lt;br /&gt;&lt;br /&gt;&lt;li&gt;A simple IDE layout for components that is similar to IntelliJ.  There are no unnecessary perspectives that Eclipse users have.&lt;br /&gt;&lt;/li&gt; &lt;li&gt;Support for auto-completion of methods with rdoc helpers.&lt;br /&gt;&lt;/li&gt; &lt;li&gt;Debugging capabilites that are simple to use.&lt;br /&gt;&lt;/li&gt; &lt;li&gt;Integration with version control repositories like Subversion.&lt;/li&gt; &lt;li&gt;Tools to manage your Gems and Plugins from the Tools menu&lt;/li&gt; &lt;li&gt;Easy navigation between Rails controllers and views as well as opening files via keystrokes.&lt;/li&gt; &lt;li&gt;Decent refactoring built in&lt;/li&gt; &lt;li&gt;Generators for creating Rails components.&lt;/li&gt; &lt;li&gt;Menus to run Rake and Migration scripts&lt;/li&gt; &lt;li&gt;Built in functionality to start a Rails console from your project&lt;/li&gt; &lt;li&gt;It works great on my Mac and much more than I expected...&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;Just to whet your appetite to get you more excited about this IDE here are a couple of screenshots from how I use this IDE:&lt;br /&gt;Here is what it looks like when editing files with code completion and quick docs:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_T5piBwJWgOI/R5J6fxT119I/AAAAAAAAAAg/xlB5RcVIIOk/s1600-h/netbeans-rails-quickdoc.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_T5piBwJWgOI/R5J6fxT119I/AAAAAAAAAAg/xlB5RcVIIOk/s400/netbeans-rails-quickdoc.jpg" alt="" id="BLOGGER_PHOTO_ID_5157319209523730386" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And here is a shot of what debugging looks like in NetBeans:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_T5piBwJWgOI/R5J62RT11-I/AAAAAAAAAAo/n_E3sLvlfvg/s1600-h/netbeans-rails-debug.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_T5piBwJWgOI/R5J62RT11-I/AAAAAAAAAAo/n_E3sLvlfvg/s400/netbeans-rails-debug.jpg" alt="" id="BLOGGER_PHOTO_ID_5157319596070787042" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you are working on a Mac make sure you look at this &lt;a href="http://wiki.netbeans.org/RubyGems"&gt;link&lt;/a&gt;  for setting up the fast debugger gem. You will also need to have XCode installed for compilation.&lt;br /&gt;I think you will agree that this is the best IDE for Rails at this time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-5575605749250590836?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/5575605749250590836/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=5575605749250590836' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/5575605749250590836'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/5575605749250590836'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2007/12/netbeans-60-and-rails.html' title='NetBeans 6.0 and Rails'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_T5piBwJWgOI/R5J6fxT119I/AAAAAAAAAAg/xlB5RcVIIOk/s72-c/netbeans-rails-quickdoc.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-2646861367639147630</id><published>2007-03-05T13:37:00.000-08:00</published><updated>2008-01-20T16:43:47.207-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JRuby'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Java Through Ruby Through Java</title><content type='html'>&lt;h3&gt;Java =&amp;gt; Ruby&lt;br /&gt;&lt;/h3&gt;Now that JRuby has matured I thought I would see what it would take to inject Java objects into Ruby objects via a Spring context.  Here is how this works:&lt;br /&gt;&lt;p&gt;First, create a Ruby class that will receive your Java objects:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;ruby_messenger.rb&lt;/i&gt;:&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;require 'java'&lt;br /&gt;&lt;br /&gt;include_class 'com.meagle.common.spring.jruby.JRubySpring'&lt;br /&gt;include_class 'com.meagle.common.service.UserService'&lt;br /&gt;include_class 'com.meagle.common.service.impl.BaseUserServiceImpl'&lt;br /&gt;&lt;br /&gt;class RubyMessenger &amp;lt; JRubySpring&lt;br /&gt;&lt;br /&gt;def printMe&lt;br /&gt; p @@message&lt;br /&gt; p @@userService.to_s&lt;br /&gt; user =  @@userService.findUser('admin')&lt;br /&gt; p "User first name: #{user.firstName}"&lt;br /&gt;   #p user.to_xml&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def setMessage(message)&lt;br /&gt;  @@message = message&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def getMessage&lt;br /&gt; @@message&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def setUserService(userService)&lt;br /&gt;  @@userService = userService&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;RubyMessenger.new # this last line is not essential (but see below)&lt;br /&gt;&lt;/pre&gt;This class will receive a user service object that allows us to make calls to a transactional Spring bean to a DAO defined in the main Spring configuration.&lt;br /&gt;&lt;p&gt;Furthermore, I am injecting a simple String into the Ruby class from the Spring context called 'message'.  Notice that the class extends an interface in Java called JRubySpring.  This interface is used to expose methods on the Ruby object to our Java application.  Here is what this Java interface looks like:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;JRubySpring.java&lt;/i&gt;:&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;pre&gt;public interface JRubySpring {&lt;br /&gt;   void printMe();&lt;br /&gt;&lt;br /&gt;   void setUserService(UserService service);&lt;br /&gt;&lt;br /&gt;   String getMessage();&lt;br /&gt;&lt;br /&gt;   void setMessage(String message);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Now we have to tell Spring about our Ruby class and the properties to inject into the class.  This can be wired up in a Spring 2.0 context configuration file like this:&lt;br /&gt;&lt;p&gt;&lt;i&gt;applicationContext-jruby.xml&lt;/i&gt;&lt;/p&gt;&lt;pre&gt;&amp;lt;beans xmlns="http://www.springframework.org/schema/beans"&lt;br /&gt;      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;      xmlns:lang="http://www.springframework.org/schema/lang"&lt;br /&gt;      xsi:schemaLocation="http://www.springframework.org/schema/beans&lt;br /&gt;http://www.springframework.org/schema/beans/spring-beans-2.0.xsd&lt;br /&gt;http://www.springframework.org/schema/lang&lt;br /&gt;http://www.springframework.org/schema/lang/spring-lang-2.0.xsd"&amp;gt;&lt;br /&gt;&lt;br /&gt;   &amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&lt;br /&gt;   &amp;lt;lang:jruby id="jrubyService"&lt;br /&gt;               script-interfaces="com.meagle.common.spring.jruby.JRubySpring"&lt;br /&gt;               script-source="classpath:ruby_messenger.rb"&amp;gt;&lt;br /&gt;&lt;br /&gt;       &amp;lt;lang:property name="message" value="Hello from Ruby!"/&amp;gt;&lt;br /&gt;&lt;br /&gt;       &amp;lt;lang:property name="userService"&amp;gt;&lt;br /&gt;           &amp;lt;ref bean="userService"/&amp;gt;&lt;br /&gt;       &amp;lt;/lang:property&amp;gt;&lt;br /&gt;&lt;br /&gt;   &amp;lt;/lang:jruby&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/beans&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;This instructs Spring to use the ruby_messenger.rb class from the classpath and allow Java classes to use the JRubySpring interface to invoke methods on the Ruby class.  Furthermore, you can see the properties that are being injected into the Ruby class here.&lt;br /&gt;&lt;p&gt;Finally, we need a way to test this out.  Again, Spring can help us test this using the &lt;i&gt;AbstractSpringIntegrationTestBase&lt;/i&gt; class to help with initializing a Spring context and running test scenarios.  Here is the test class:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;JRubySpringTest.java&lt;/i&gt;&lt;/p&gt;&lt;pre&gt;public class JRubySpringTest extends AbstractSpringIntegrationTestBase {&lt;br /&gt;   private UserService userService;&lt;br /&gt;&lt;br /&gt;   public JRubySpringTest() {&lt;br /&gt;       setDefaultRollback(true);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   protected String[] getConfigLocations() {&lt;br /&gt;       return new String[]{&lt;br /&gt;               "applicationContext-core.xml",&lt;br /&gt;               "applicationContext-jruby.xml"&lt;br /&gt;       };&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   JRubySpring jrubyService;&lt;br /&gt;&lt;br /&gt;   public void testGetUser() {&lt;br /&gt;       System.out.println("Printing messages from plain old Java Spring context...");&lt;br /&gt;       System.out.println(jrubyService.getMessage());&lt;br /&gt;       User user =  userService.findUser("admin");&lt;br /&gt;       System.out.println("user = " + user.getFirstName());&lt;br /&gt;       System.out.println("Printing messages from a Spring context injected&lt;br /&gt;                           into a Ruby object...");&lt;br /&gt;       jrubyService.printMe();&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public JRubySpring getJrubyService() {&lt;br /&gt;       return jrubyService;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void setJrubyService(JRubySpring jrubyService) {&lt;br /&gt;       this.jrubyService = jrubyService;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void setUserService(UserService userService) {&lt;br /&gt;       this.userService = userService;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;This test class will initialize Spring and autowire the Ruby class into the test class with the Java/JRuby properties  injected into the Ruby class as singletons.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Here is some sample output when running this test class:&lt;br /&gt;&lt;p&gt;03.05.2007 13:30:40,062 INFO  dao.hibernate.JRubySpringTest.startNewTransaction:318  -&amp;gt;&lt;br /&gt;Began transaction (1): transaction manager [org.springframework.orm.hibernate3.HibernateTransactionManager@1284f8e]; default rollback = true&lt;br /&gt;&lt;br /&gt;Printing messages from plain old Java Spring context...&lt;br /&gt;&lt;br /&gt;Hello from Ruby!&lt;br /&gt;&lt;br /&gt;user = System&lt;br /&gt;&lt;br /&gt;Printing messages from a Spring context injected into a Ruby object...&lt;br /&gt;&lt;br /&gt;"Hello from Ruby!"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;"com.meagle.common.service.impl.BaseUserServiceImpl@595bcd"&lt;br /&gt;&lt;br /&gt;"User first name: System"&lt;br /&gt;&lt;br /&gt;03.05.2007 13:30:40,312 INFO  dao.hibernate.JRubySpringTest.endTransaction:284  -&amp;gt;&lt;br /&gt;Rolled back transaction after test execution&lt;br /&gt;03.05.2007 13:30:40,328 INFO  context.support.ClassPathXmlApplicationContext.doClose:599  -&amp;gt;&lt;br /&gt;Closing application context [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=6504030]&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;h3&gt;Ruby =&amp;gt; Java&lt;/h3&gt;So what if you want to have an irb session and communicate with your Java objects.  For this you just need your existing Spring context and a bootstrapping file for Ruby to initialize a JRuby console.  It goes like this:&lt;br /&gt;&lt;p&gt;&lt;i&gt;jruby_console.rb&lt;/i&gt;&lt;/p&gt;&lt;pre&gt;# To use JRuby and Java Spring contexts together do the following:&lt;br /&gt;#&lt;br /&gt;# 1. Download the JRuby irb jar file from:&lt;br /&gt;#         http://jruby.codehaus.org/Running+the+JRuby+Console+(graphical+IRB)&lt;br /&gt;#&lt;br /&gt;# 2. Add the jruby-console-0.9.2.jar file to your Java project classpath&lt;br /&gt;#&lt;br /&gt;# 3. Create a new configuration to run a Java application and set the main class to&lt;br /&gt;#         org.jruby.demo.IRBConsole&lt;br /&gt;#&lt;br /&gt;# 4. Of course make sure your classpath is set correctly for your Spring config and&lt;br /&gt;#    other classes/jars you need to test.&lt;br /&gt;#&lt;br /&gt;# 5. Run the application which will load the JRuby irb console&lt;br /&gt;#&lt;br /&gt;# 6. In the irb window type &amp;gt; load 'jruby_console.rb' to initialize this script.&lt;br /&gt;#&lt;br /&gt;# 7. This will load your Spring configuration.  Access your Spring beans as&lt;br /&gt;#    instance variables like this @beanName.&lt;br /&gt;#&lt;br /&gt;# 8. That was easy&lt;br /&gt;&lt;br /&gt;require 'java'&lt;br /&gt;&lt;br /&gt;# require this file if you want to include your domain objects instead of&lt;br /&gt;# including them one at a time.&lt;br /&gt;require 'domain_core.rb'&lt;br /&gt;&lt;br /&gt;# include this line to include all of your specific Java domain objects&lt;br /&gt;include Domain::Core&lt;br /&gt;&lt;br /&gt;include_class('org.springframework.context.support.ClassPathXmlApplicationContext')&lt;br /&gt;&lt;br /&gt;springConfigs = ["applicationContext-core.xml"]&lt;br /&gt;@context = ClassPathXmlApplicationContext.new(springConfigs.join(","))&lt;br /&gt;&lt;br /&gt;if @context.nil?&lt;br /&gt;p "*********Could Not initialize Spring context*********"&lt;br /&gt;&lt;br /&gt;return&lt;br /&gt;else&lt;br /&gt;p "*********Spring context initialized as @context*********"&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;p "(#{@context.beanDefinitionNames.length}) beans initialized in this context:"&lt;br /&gt;beans = ""&lt;br /&gt;&lt;br /&gt;# Assign non-abstract beans are initialized to instance variables.&lt;br /&gt;# Example: The 'userService' bean name will be accessible via the&lt;br /&gt;#          instance variable  '@userService'&lt;br /&gt;@context.beanDefinitionNames.sort.each do |bean|&lt;br /&gt;unless bean =~ /^abstract/&lt;br /&gt;  instance_variable_set("@#{bean}", @context.getBean("#{bean}"))&lt;br /&gt;  beans &amp;lt;&amp;lt; bean + ", "&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;p beans&lt;br /&gt;p "=========================================================================="&lt;br /&gt;p "Hey you - access your beans as instance variable like this @springBeanName"&lt;br /&gt;p "=========================================================================="&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Now you can do sadistic stuff like this:&lt;/p&gt;&lt;pre&gt; Welcome to the JRuby IRB Console&lt;br /&gt;&lt;br /&gt;irb(main):001:0&amp;gt; load 'jruby_console.rb'&lt;br /&gt;"*********Spring context initialized as @context*********"&lt;br /&gt;"(4) beans initialized in this context:"&lt;br /&gt;dataSource, sessionFactory, userDAO, userService"&lt;br /&gt;"=========================================================================="&lt;br /&gt;"Hey you - access your beans as instance variable like this @springBeanName"&lt;br /&gt;"=========================================================================="&lt;br /&gt;=&amp;gt; true&lt;br /&gt;&lt;br /&gt;irb(main):002:0&amp;gt; user = @userService.find(33, User.new.class)&lt;br /&gt;=&amp;gt; #&lt;domain::core::user:0xea01d7 java_object="com.meagle.common.bo.user.User@1b5eaed"&gt;&lt;br /&gt;&lt;br /&gt;irb(main):003:0&amp;gt; p user.firstName&lt;br /&gt;"Mark"&lt;br /&gt;=&amp;gt; nil&lt;br /&gt;irb(main):004:0&amp;gt;&lt;br /&gt;&lt;/domain::core::user:0xea01d7&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-2646861367639147630?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/2646861367639147630/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=2646861367639147630' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/2646861367639147630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/2646861367639147630'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2007/03/java-through-ruby-through-java.html' title='Java Through Ruby Through Java'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-8936110484460470526</id><published>2006-12-05T14:13:00.000-08:00</published><updated>2008-01-20T16:44:15.325-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Autocompleting with Stripes and Scriptaculous</title><content type='html'>I recently had to implement autocompletion AJAX functionality with &lt;a href="http://script.aculo.us/"&gt;script.aculous.us&lt;/a&gt; and &lt;a href="http://stripes.mc4j.org/"&gt;Stripes&lt;/a&gt;.  Once again this AJAX functionality was simple to integrate with Stripes.  This code will return a list of users to select from after the user types more than two characters.&lt;br /&gt;&lt;br /&gt;Here is an example Stripes action that will be responsible for looking up the users and returning a partial page with the unorder list of users.  In this example code the autoCompleteText will capture the text that will be autocompleted.  A Spring bean will be used to call out to service which will use a Hibernate DAO to query for the user list.&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;div class="codeContent"&gt;&lt;pre class="code-java"&gt; &lt;span class="code-keyword"&gt;public class&lt;/span&gt; UserLookupAction &lt;span class="code-keyword"&gt;implements&lt;/span&gt; ActionBean {&lt;br /&gt;&lt;br /&gt;  String autoCompleteText;&lt;br /&gt;&lt;br /&gt;  UserService userService;&lt;br /&gt;  &lt;span class="code-keyword"&gt;private&lt;/span&gt; List&lt;user&gt; users;&lt;br /&gt;&lt;br /&gt;  &lt;span class="code-keyword"&gt;public&lt;/span&gt; Resolution findUsers(){&lt;br /&gt;   &lt;span class="code-keyword"&gt;this&lt;/span&gt;.users = userService.findUsersByAutocompletion(autoCompleteText);&lt;br /&gt;   &lt;span class="code-keyword"&gt;return new&lt;/span&gt; ForwardResolution(&lt;span class="code-quote"&gt;"/pages/useradmin/FindUsers.jsp"&lt;/span&gt;);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @SpringBean&lt;br /&gt;  &lt;span class="code-keyword"&gt;public void&lt;/span&gt; setUserService(UserService userService) {&lt;br /&gt;   &lt;span class="code-keyword"&gt;this&lt;/span&gt;.userService = userService;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  &lt;span class="code-keyword"&gt;public&lt;/span&gt; String getAutoCompleteText() {return autoCompleteText;}&lt;br /&gt;&lt;br /&gt;  &lt;span class="code-keyword"&gt;public void&lt;/span&gt; setAutoCompleteText(String autoCompleteText) {&lt;br /&gt;   &lt;span class="code-keyword"&gt;this&lt;/span&gt;.autoCompleteText = autoCompleteText;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  &lt;span class="code-keyword"&gt;public&lt;/span&gt; List&lt;user&gt; getUsers() {&lt;br /&gt;   return users;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  &lt;span class="code-keyword"&gt;public void&lt;/span&gt; setActionBeanContext(ActionBeanContext actionBeanContext);&lt;br /&gt;  &lt;span class="code-keyword"&gt;public&lt;/span&gt; ActionBeanContext getActionBeanContext() {&lt;span class="code-keyword"&gt;return this&lt;/span&gt;.actionBeanContext;}&lt;br /&gt; }&lt;br /&gt;&lt;/user&gt;&lt;/user&gt;&lt;/pre&gt; &lt;/div&gt;&lt;/div&gt;&lt;br /&gt;This JSP is responsible for making the AJAX calls to the Stripes action above.  The AJAX.Autocompleter code from Scriptaculous does the work when more than two characters are present in the text field named autocomplete.  If there are results they are returned in a partial page as an unordered list (more later).  An animated gif indicator is used to signal the user that the AJAX call is being made and searching for results.  Also, there is a callback method called getSelectionId that is called afterUpdateElement.  This will be used to set the selected user id in a hidden field named autocompleteUserId.  This will be used when editing the user through another Stripes action (UserEditAction).&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;div class="codeContent"&gt;&lt;pre class="code-java"&gt; &amp;lt;style type=&lt;span class="code-quote"&gt;"text/css"&lt;/span&gt;&amp;gt;&lt;br /&gt;          div.autocomplete {&lt;br /&gt;            position:absolute;&lt;br /&gt;            background-color:white;&lt;br /&gt;            border:1px solid #888;&lt;br /&gt;            margin:0px;&lt;br /&gt;            padding:0px;&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          div.autocomplete ul {&lt;br /&gt;            list-style-type:none;&lt;br /&gt;            margin:0px;&lt;br /&gt;            padding:0px;&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          div.autocomplete ul li.selected { background-color: #ffb;}&lt;br /&gt;&lt;br /&gt;          div.autocomplete ul li {&lt;br /&gt;            list-style-type:none;&lt;br /&gt;            display:block;&lt;br /&gt;            margin:0;&lt;br /&gt;            padding:5px;&lt;br /&gt;            cursor:pointer;&lt;br /&gt;            border-bottom: 1px solid #cbcbcb;&lt;br /&gt;          }&lt;br /&gt; &amp;lt;/style&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;%@ taglib prefix=&lt;span class="code-quote"&gt;"stripes"&lt;/span&gt; uri=&lt;span class="code-quote"&gt;"http://stripes.sourceforge.net/stripes.tld"&lt;/span&gt; %&amp;gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="code-tag"&gt;&amp;lt;stripes:form beanclass=&lt;/span&gt;&lt;span class="code-quote"&gt;"com.meagle.web.stripes.action.useradmin.UserEditAction"&lt;/span&gt;&lt;span class="code-tag"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;           User Name:&lt;br /&gt;           &lt;span class="code-tag"&gt;&amp;lt;stripes:text size=&lt;/span&gt;&lt;span class="code-quote"&gt;"30"&lt;/span&gt; &lt;span class="code-tag"&gt;id=&lt;/span&gt;&lt;span class="code-quote"&gt;"autocomplete"&lt;/span&gt; &lt;span class="code-tag"&gt;name=&lt;/span&gt;&lt;span class="code-quote"&gt;"autoCompleteText"&lt;/span&gt;&lt;span class="code-tag"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;           &lt;span class="code-tag"&gt;&amp;lt;div id=&lt;/span&gt;&lt;span class="code-quote"&gt;"autocomplete_choices"&lt;/span&gt; &lt;span class="code-tag"&gt;class=&lt;/span&gt;&lt;span class="code-quote"&gt;"autocomplete"&lt;/span&gt;&amp;gt;&lt;span class="code-tag"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br /&gt;           &lt;span class="code-tag"&gt;&amp;lt;script type=&lt;/span&gt;&lt;span class="code-quote"&gt;"text/javascript"&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;           new Ajax.Autocompleter (&lt;span class="code-quote"&gt;"autocomplete"&lt;/span&gt;,&lt;br /&gt;           &lt;span class="code-quote"&gt;"autocomplete_choices"&lt;/span&gt;,&lt;br /&gt;           &lt;span class="code-quote"&gt;"${pageContext.request.contextPath}/useradmin/UserLookup.action?findUsers="&lt;/span&gt;,&lt;br /&gt;           {minChars: 2, afterUpdateElement : getSelectionId, indicator: &lt;span class="code-quote"&gt;'indicator1'&lt;/span&gt;});&lt;br /&gt;&lt;br /&gt;           function getSelectionId(text, li) {&lt;br /&gt;              $(&lt;span class="code-quote"&gt;'autocompleteUserId'&lt;/span&gt;).value = li.id;&lt;br /&gt;           }&lt;br /&gt;           &lt;span class="code-tag"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;           &lt;span class="code-tag"&gt;&amp;lt;stripes:hidden name=&lt;/span&gt;&lt;span class="code-quote"&gt;"userId"&lt;/span&gt; &lt;span class="code-tag"&gt;id=&lt;/span&gt;&lt;span class="code-quote"&gt;"autocompleteUserId"&lt;/span&gt;/&amp;gt;&lt;br /&gt;           &lt;span class="code-tag"&gt;&amp;lt;stripes:submit name=&lt;/span&gt;&lt;span class="code-quote"&gt;"editUser"&lt;/span&gt; class=&lt;span&gt;&lt;span class="code-tag"&gt;class=&lt;/span&gt;"code-quote"&amp;gt;"textBox"&lt;/span&gt; &lt;span class="code-tag"&gt;value=&lt;/span&gt;&lt;span class="code-quote"&gt;"Edit"&lt;/span&gt;/&amp;gt;&lt;br /&gt;&lt;br /&gt;           &lt;span class="code-tag"&gt;&amp;lt;span id=&lt;/span&gt;&lt;span class="code-quote"&gt;"indicator1"&lt;/span&gt; &lt;span class="code-tag"&gt;style=&lt;/span&gt;&lt;span class="code-quote"&gt;"display: none"&amp;gt;&lt;/span&gt;&lt;span class="code-tag"&gt;&lt;br /&gt;           &amp;lt;img width=&lt;/span&gt;&lt;span class="code-quote"&gt;"14"&lt;/span&gt; &lt;span class="code-tag"&gt;height=&lt;/span&gt;&lt;span class="code-quote"&gt;"14"&lt;/span&gt;&lt;br /&gt;           &lt;span class="code-tag"&gt;src=&lt;/span&gt;&lt;span class="code-quote"&gt;"${pageContext.request.contextPath}/images/indicator_circle_ball.gif"&lt;/span&gt;&lt;br /&gt;           &lt;span class="code-tag"&gt;alt=&lt;/span&gt;&lt;span class="code-quote"&gt;"Working..."&lt;/span&gt; /&amp;gt;&lt;br /&gt;&lt;br /&gt; &lt;span class="code-tag"&gt;&amp;lt;/stripes:form&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;/pre&gt; &lt;/div&gt;&lt;/div&gt;Finally, this is the partial page that is returned by the UserLookupAction when results are returned.  This simply iterates over the User objects from the Stripes action and creates an unorder list that is stylized with the CSS above.&lt;br /&gt;&lt;div class="code"&gt;&lt;div class="codeContent"&gt;&lt;br /&gt;  &lt;pre class="code-java"&gt; &lt;span class="code-tag"&gt;&amp;lt;%@ taglib prefix=&lt;span class="code-quote"&gt;"c"&lt;/span&gt; uri=&lt;span class="code-quote"&gt;&amp;lt;http://java.sun.com/jsp/jstl/core&amp;gt;&lt;/span&gt; %&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;ul&amp;gt;&lt;br /&gt;         &amp;lt;c:forEach var=&lt;span class="code-quote"&gt;"user"&lt;/span&gt; items=&lt;span class="code-quote"&gt;"${ actionBean.users}"&lt;/span&gt;&amp;gt;&lt;br /&gt;            &amp;lt;li id=&lt;span class="code-quote"&gt;"${user.id}"&lt;/span&gt;&amp;gt;${user.firstName} ${user.lastName}&amp;lt;/li&amp;gt;&lt;br /&gt;         &amp;lt;/c:forEach&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;/ul&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-8936110484460470526?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/8936110484460470526/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=8936110484460470526' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/8936110484460470526'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/8936110484460470526'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2006/11/autocompleting-with-stripes-and.html' title='Autocompleting with Stripes and Scriptaculous'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-6243187419688918584</id><published>2006-11-23T17:17:00.000-08:00</published><updated>2008-01-20T16:44:15.325-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>AJUG Stripes Presentation</title><content type='html'>Phil Barnes and I presented the Stripes framework at AJUG last night. We wanted to spread the word about Stripes and how it simplifies Java Web development. Most of the people who attended the presentation were using Struts as their presentation framework (no surprise here). We came prepared with many examples about how Stripes compared to Struts.&lt;br /&gt;&lt;br /&gt;I think that the presentation was well received and hopefully it will make people take another look at Stripes. We have researched many Java Web frameworks and this is probably the best one out there as far as simplicity while allowing the developer to focus on application development. Stripes uses convention over configuration for resolving URLs to action classes, has excellent databinding, and integrated well with other POJO based frameworks. &lt;br /&gt;&lt;br /&gt;Please contact me at meagle@gmail.com if you want a copy of the slides.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-6243187419688918584?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/6243187419688918584/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=6243187419688918584' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/6243187419688918584'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/6243187419688918584'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2006/11/ajug-stripes-presentation.html' title='AJUG Stripes Presentation'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-5933857541281709551</id><published>2006-04-28T17:20:00.000-07:00</published><updated>2008-01-20T16:44:15.325-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Stripes: I think we have a winner!</title><content type='html'>For some time now I have been evaluating Java web frameworks. This was mostly driven by my frustration with existing frameworks I have been using. The amount of effort it takes to feed these frameworks to produce anything meaningful is usually painful and not fun to maintain. My baseline framework for Java Web development has been Struts. Struts is just outdated and requires way too many configuration files, extra classes outside the controller/action, and a separate ActionForm class that requires crude object binding. Enough said. From Struts I investigated a slew of frameworks such as Tapestry, Wicket, Spring MVC, and some JSF. These frameworks were definitely better than Struts (not too hard to achieve) but never felt much more productive and varied in learning curves. Then I started to investigate WebWork. This was definitely a step in the right direction. WebWork is an action based framework that is the basis for the Struts Action 2.0 Framework. WebWork does offer simpler actions, less configuration, some AJAX support, different view resolvers, interceptor stacks, and much more. We even decided to use WebWork for our new project after we felt that it addressed most of our concerns and there was nothing better to work with. We did experience more than a 40%+ reduction in the number lines of code and configuration files we had to write. This was great until...&lt;br /&gt;&lt;br /&gt;The Stripes project recently released version 1.3 of their web framework. This framework relies heavily on Java 5 capabilities to deliver a much simpler web framework. The configuration is done up front in the web.xml file where a filter and a Servlet dispatcher is configured. The only thing that you need to be aware of is to configure the root package of your Stripes action classes. This allows Stripes to inspect your action classes when your servlet container starts up. Stripes uses a convention based approach to determine which classes in this defined package will be Stripes actions. Therefore, action classes do not have to be configured in an XML file. That's right - no more struts-config, xwork.xml, JSF config nonsense, etc.&lt;br /&gt;&lt;br /&gt;From this point on you are ready to code. The action classes that you build contain concise annotations that handle interception, validation, Spring integration, and more. With your classes annotated you do not have to flip back and forth between external files and your action classes. This saves you a ton of time and reduces lines of code. In fact we migrated our WebWork code to Stripes equivalents and experienced another 20%+ reduction in the lines of code (and removing configuration files) while maintaining full functionality. Not too bad. The migration only took about 2 days to complete for about 8 complex actions and 12 JSPs. As you can see will be using this on all of our projects.&lt;br /&gt;&lt;br /&gt;The only con I can think of for Stripes is that you have to use JSP and the Stripes taglibs. However, the taglib is pretty good and very close to HTML in regards to the naming used. I am hoping that a Velocity view resolver is on the horizon in the not too distant future. However, I did not experience too many problems with JSTL and the Stripes taglib.&lt;br /&gt;&lt;br /&gt;If this sounds good take a look at the Stripes Quick Start Guide to quickly get up and running. Stripes is a great step forward for Java web development. It offers Java developers an simple way to program Java web applications with minimal configuration, high productivity, and a fun development framework that is addictive. This might be as close to the Ruby on Rails Action Pack for MVC development as we will see in a while from the Java community.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-5933857541281709551?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/5933857541281709551/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=5933857541281709551' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/5933857541281709551'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/5933857541281709551'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2006/04/stripes-i-think-we-have-winner.html' title='Stripes: I think we have a winner!'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-7169047861711602628</id><published>2006-03-28T17:23:00.000-08:00</published><updated>2008-01-20T16:44:15.326-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Comparing Web Frameworks: WebWork</title><content type='html'>Lately there have been Java Web framework comparisons on &lt;a href="http://www.theserverside.com/"&gt;theServerSide.com&lt;/a&gt;.  These comparisons revolve around requirements on &lt;a href="http://weblogs.java.net/blog/simongbrown/archive/2005/11/comparing_webap_1.html"&gt;Simon Brown's Blog.&lt;/a&gt;  I am not sure that the application is very relevant and has limited functionality.  However, I decided I would try this with WebWork.  I have been using WebWork for about 5 months now and have been very productive with the framework's capabilities and ease of use.  I took the requirements for the read only blog and reproduced it with WebWork.  There is really very little that needed to be coded to make this happen and I did this on a plane trip from Atlanta to Las Vegas where I am attending TSSS with only one battery in my notebook.&lt;br /&gt;&lt;p&gt;&lt;/p&gt;To save time I copied some of the components from other people's implementations that were not framework specific.This included the domain objects &lt;code&gt;Blog&lt;/code&gt;, &lt;code&gt;BlogEntry&lt;/code&gt;, and &lt;code&gt;BlogService&lt;/code&gt;.  Theseare straightforward and I could have generated them in IntelliJ in about 2 minutes so no big deal.  I also copied the stylesheet and image that was used to pretty up the blog look and feel.  Again, no big deal and not really relevant to the framework comparison.  So let me take a moment to preface some of my gripes with this little project.  First, I do not consider someof the methods I used to code this application to be ideal.  Since this is a read only blog application I did not use a persistence mechanism for the blog entries.&lt;br /&gt;&lt;p&gt;   Nevertheless, I coded the application to the specifications required using only the WebWork web tier components no more.  The main WebWork component that needed to be designed was the action class.  This is fairly lean and should be&lt;br /&gt;easy to interpret.  Here is the code:&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;public class BlogAction implements Preparable {&lt;br /&gt;&lt;br /&gt;private Blog blog;&lt;br /&gt;private BlogEntry blogEntry;&lt;br /&gt;private String blogEntryId;&lt;br /&gt;private BlogService blogService;&lt;br /&gt;&lt;br /&gt;public void prepare() throws Exception {&lt;br /&gt;  blog = getBlogService().getBlog();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public String execute() {&lt;br /&gt;  // simply display the default page -- the data was setup in prepare()&lt;br /&gt;  return "success";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public String viewBlogEntry() {&lt;br /&gt;  this.blogEntry = blog.getBlogEntry(this.blogEntryId);&lt;br /&gt;&lt;br /&gt;  return "entry";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void setBlogEntryId(String blogEntryId) {&lt;br /&gt;  this.blogEntryId = blogEntryId;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public Blog getBlog() { return blog; }&lt;br /&gt;&lt;br /&gt;public BlogEntry getBlogEntry() { return blogEntry; }&lt;br /&gt;&lt;br /&gt;protected BlogService getBlogService() {&lt;br /&gt;  if (blogService == null) {&lt;br /&gt;      blogService = new BlogService();&lt;br /&gt;  }&lt;br /&gt;  return blogService;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void setBlogService(BlogService blogService) {&lt;br /&gt;  this.blogService = blogService;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is about 40 lines of code and most of that is comments, fields, and accessors.  Really there are only about 5 lines of code of any logic.  The &lt;code&gt;prepare&lt;/code&gt; method is called before the action executes to setup the action.  This gets called because the action implements the &lt;code&gt;Preparable&lt;/code&gt; interface.  The &lt;a href="http://www.opensymphony.com/webwork/wikidocs/Prepare%20Interceptor.html"&gt;Prepare &lt;/a&gt;interceptor is invoked as one of the interceptors in the default interceptor stack. The service should really be injected into the action by an IoC container such as Spring (WebWork has good integration support with Spring).  Also notice that other than implementing the &lt;code&gt;Preparable&lt;/code&gt; interface this action is just a POJO.&lt;br /&gt;&lt;br /&gt;Here is the action configuration in the xwork.xml file:&lt;br /&gt;&lt;pre&gt;   &amp;lt;action name="Blog" class="com.meagle.wwblog.web.webwork.actions.BlogAction"&amp;gt;&lt;br /&gt;      &amp;lt;result name="success"&amp;gt;/pages/index.vm&amp;lt;/result&amp;gt;&lt;br /&gt;      &amp;lt;result name="blogentry"&amp;gt;/pages/entry.vm&amp;lt;/result&amp;gt;&lt;br /&gt;  &amp;lt;/action&amp;gt;&lt;br /&gt;&lt;/pre&gt;As for the views there were two files.  The first one shows the blog and associated entries.  I used SiteMesh todecorate the common elements and JSP as the view resolver to display the stack elements. WebWork can also use Freemarker, Velocity, and other view resolvers as well. Here is what the Sitemesh decorator looks like:&lt;br /&gt;&lt;pre&gt;     &amp;lt;%@ page pageEncoding="UTF-8"%&amp;gt;&lt;br /&gt;  &amp;lt;%@ page contentType="text/html; charset=utf-8"%&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %&amp;gt;&lt;br /&gt;  &amp;lt;%@ taglib prefix="ww" uri="/webwork" %&amp;gt;&lt;br /&gt;  &amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;br /&gt;  &amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"&lt;br /&gt;                        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&amp;gt;&lt;br /&gt;  &amp;lt;html xmlns="http://www.w3.org/1999/xhtml"&lt;br /&gt;        xml:lang="en"&lt;br /&gt;        lang="en"&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;head&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/&amp;gt;&lt;br /&gt;      &amp;lt;title&amp;gt;&amp;lt;decorator:title default="Welcome!" /&amp;gt;&amp;lt;/title&amp;gt;&lt;br /&gt;      &amp;lt;meta http-equiv="Content-Style-Type" content="text/css"&amp;gt;&lt;br /&gt;      &amp;lt;link rel="stylesheet" href="/css/screen.css" type="text/css"/&amp;gt;&lt;br /&gt;  &amp;lt;/head&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;body&amp;gt;&lt;br /&gt;  &amp;lt;div id="container"&amp;gt;&lt;br /&gt;      &amp;lt;h1&amp;gt;&amp;lt;decorator:title default="Welcome!" /&amp;gt;&amp;lt;/h1&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;h2&amp;gt;&amp;lt;ww:property value="%{blog.description}"/&amp;gt;&amp;lt;/h2&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;decorator:body /&amp;gt;&lt;br /&gt;  &amp;lt;/div&amp;gt;&lt;br /&gt;  &amp;lt;/body&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;   So this takes care of the blog title, name, and description that appears on each page.  This also leaves a placeholder for the content of each page.  Here is what was required to display the entries in the blog:&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;     &amp;lt;%@ page pageEncoding="UTF-8"%&amp;gt;&lt;br /&gt;  &amp;lt;%@ page contentType="text/html; charset=utf-8"%&amp;gt;&lt;br /&gt;  &amp;lt;%@ taglib uri="/webwork" prefix="ww" %&amp;gt;&lt;br /&gt;  &amp;lt;html&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;head&amp;gt;&lt;br /&gt;          &amp;lt;title&amp;gt;&amp;lt;ww:property value="%{blog.name}"/&amp;gt;&amp;lt;/title&amp;gt;&lt;br /&gt;      &amp;lt;/head&amp;gt;&lt;br /&gt;      &amp;lt;body&amp;gt;&lt;br /&gt;          &amp;lt;table&amp;gt;&lt;br /&gt;&lt;br /&gt;              &amp;lt;ww:iterator value="blog.blogEntries" id="blogEntry"&amp;gt;&lt;br /&gt;              &amp;lt;tr&amp;gt;&lt;br /&gt;                  &amp;lt;td&amp;gt;&lt;br /&gt;                      &amp;lt;div class="blogEntry"&amp;gt;&lt;br /&gt;                          &amp;lt;h3&amp;gt;&amp;lt;ww:property value="title"/&amp;gt;&amp;lt;/h3&amp;gt;&lt;br /&gt;&lt;br /&gt;                          &amp;lt;div&amp;gt;&lt;br /&gt;                              &amp;lt;ww:if test="excerpt != null"&amp;gt;&lt;br /&gt;                                  &amp;lt;ww:property value="excerpt" escape="false"/&amp;gt;&lt;br /&gt;                              &amp;lt;/ww:if&amp;gt;&lt;br /&gt;                              &amp;lt;ww:else&amp;gt;&lt;br /&gt;                                  &amp;lt;ww:property value="body" escape="false"/&amp;gt;&lt;br /&gt;&lt;br /&gt;                              &amp;lt;/ww:else&amp;gt;&lt;br /&gt;                          &amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;                          &amp;lt;p&amp;gt;&lt;br /&gt;                              &amp;lt;ww:if test="excerpt != null"&amp;gt;&lt;br /&gt;                                  &amp;lt;ww:url id="readMoreUrl" action="Blog!viewBlogEntry"&amp;gt;&lt;br /&gt;                                      &amp;lt;ww:param name="blogEntryId" value="id"/&amp;gt;&lt;br /&gt;&lt;br /&gt;                                  &amp;lt;/ww:url&amp;gt;&lt;br /&gt;                                  &amp;lt;a href="&amp;lt;ww:property value="readMoreUrl" escape="false"/&amp;gt;"&amp;gt;Read More&amp;lt;/a&amp;gt;&lt;br /&gt;                              &amp;lt;/ww:if&amp;gt;&lt;br /&gt;                          &amp;lt;/p&amp;gt;&lt;br /&gt;&lt;br /&gt;                          &amp;lt;p&amp;gt;&lt;br /&gt;                              Posted on &amp;lt;ww:date name="%{date}" format="dd MMMM yyyy hh:mm:ss z"/&amp;gt;&lt;br /&gt;                          &amp;lt;/p&amp;gt;&lt;br /&gt;                      &amp;lt;/div&amp;gt;&lt;br /&gt;                  &amp;lt;/td&amp;gt;&lt;br /&gt;              &amp;lt;/tr&amp;gt;&lt;br /&gt;&lt;br /&gt;              &amp;lt;/ww:iterator&amp;gt;&lt;br /&gt;          &amp;lt;/table&amp;gt;&lt;br /&gt;      &amp;lt;/body&amp;gt;&lt;br /&gt;  &amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;   Again, this is straightforward and captures the requirements of the application.  The only interesting lines here that allows the user to conditionally view more of a blog entry is this:&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;      &amp;lt;ww:url id="readMoreUrl" action="Blog!viewBlogEntry"&amp;gt;&lt;br /&gt;      &amp;lt;ww:param name="blogEntryId" value="id"/&amp;gt;&lt;br /&gt;  &amp;lt;/ww:url&amp;gt;&lt;br /&gt;  &amp;lt;a href="&amp;lt;ww:property value="readMoreUrl" escape="false"/&amp;gt;"&amp;gt;Read More&amp;lt;/a&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;These lines create a link that calls the &lt;code&gt;viewBlogEntry&lt;/code&gt; method of the Blog action by using a WebWorkconvention.  The blog entry ID is appended as a parameter so that we can display the correct blog entry.  You may notice the "action" in the &lt;ww:url&gt; tag references our Action class method viewBlogEntry(); it's in this method that we pull the specific entry and expose it (via a class variable) so that the view layer can render the entry.  Here is the code for the view for displaying the entry.&lt;br /&gt;&lt;/ww:url&gt;&lt;br /&gt;&lt;pre&gt;     &amp;lt;%@ page pageEncoding="UTF-8"%&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;%@ page contentType="text/html; charset=utf-8"%&amp;gt;&lt;br /&gt;  &amp;lt;%@ taglib prefix="ww" uri="/webwork" %&amp;gt;&lt;br /&gt;  &amp;lt;html&amp;gt;&lt;br /&gt;      &amp;lt;body&amp;gt;&lt;br /&gt;          &amp;lt;ww:if test="blogEntry != null"&amp;gt;&lt;br /&gt;              &amp;lt;div class="blogEntry"&amp;gt;&lt;br /&gt;&lt;br /&gt;                  &amp;lt;h3&amp;gt;&amp;lt;ww:property value="blogEntry.title" escape="false"/&amp;gt;&amp;lt;/h3&amp;gt;&lt;br /&gt;&lt;br /&gt;                  &amp;lt;div&amp;gt;&amp;lt;ww:property value="blogEntry.body" escape="false"/&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;                  &amp;lt;p&amp;gt;&lt;br /&gt;                      Posted on &amp;lt;ww:date name="%{blogEntry.date}" format="dd MMMM yyyy hh:mm:ss z"/&amp;gt;&lt;br /&gt;&lt;br /&gt;                  &amp;lt;/p&amp;gt;&lt;br /&gt;              &amp;lt;/div&amp;gt;&lt;br /&gt;          &amp;lt;/ww:if&amp;gt;&lt;br /&gt;          &amp;lt;ww:else&amp;gt;&lt;br /&gt;              &amp;lt;div class="blogEntry"&amp;gt;&lt;br /&gt;              &amp;lt;h3&amp;gt;Sorry we could not locate that blog entry...&amp;lt;/h3&amp;gt;&lt;br /&gt;&lt;br /&gt;          &amp;lt;/div&amp;gt;&lt;br /&gt;          &amp;lt;/ww:else&amp;gt;&lt;br /&gt;          &amp;lt;div&amp;gt;&amp;lt;a href="JavaScript:void(0)" onclick="history.go(-1)"&amp;gt;Back&amp;lt;/a&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;      &amp;lt;/body&amp;gt;&lt;br /&gt;  &amp;lt;/html&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;   That is really all there was to coding the requirements of the application.  The rest of the components were skeleton elements from a base WebWork configuration.&lt;br /&gt;&lt;/p&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;While this is a very simplistic web application with little functionality it does show how little coding is required by WebWork to make it happen.  There is so much more that I would have like to have shown such as some of the data binding capabilities inside WebWork when submitting form information with complex object graph information.&lt;br /&gt;&lt;p&gt;   This trivial application really does not show WebWork's full capabilities. If you think there is value in this example then you should give WebWork a try on more complex applications.&lt;br /&gt;WebWork makes coding web development with Java simple and very productive with the framework's capabilities and ease of use.&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-7169047861711602628?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/7169047861711602628/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=7169047861711602628' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/7169047861711602628'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/7169047861711602628'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2006/03/comparing-web-frameworks-webwork.html' title='Comparing Web Frameworks: WebWork'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-5953994240324411224</id><published>2005-10-25T17:39:00.000-07:00</published><updated>2008-01-20T16:44:15.326-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Java Web Framework Changes...</title><content type='html'>For some time now I have been having major frustrations deciding on a Java Web framework to replace Struts. In the past I have evaluated some alternatives such as some component based Web frameworks such as Tapestry and a little at JSF. Both of these solutions are powerful but can be quite complex. While I understand the benefits of component based frameworks I am not sure that either of these frameworks are absolutely necessary for our environment. So in between Tapestry and Struts there are over 50 other web frameworks that are slight derivatives of Struts, promising frameworks like Wicket with little doco, or strangely named frameworks with little substance. So of these frameworks only two came into consideration - Spring MVC and WebWork. Both of these frameworks are not component based but address the shortcomings of Struts. One of my biggest grips with Struts is having to implement ActionForm objects and dealing with the Action class with it's hard to unit test methods. Just binding Strings to ActionForms and translating those String to my model objects is a huge coding overhead. Spring MVC and WebWork allieviate this problem and add much more value. Both have support for continuations, AJAX, good documentation, Spring integration for actions, Velocity/Freemarker support, and community support. We currently use Spring in our middle tier and Hibernate for our persistence layer. However, we chose WebWork because of the simple configuration, easy to configure interceptors, pluggable view handlers, programmable continuations, and support for other popular frameworks like Spring. Choosing a Java web framework is very difficult and you have to accept that there is no single best choice. That is probably why there are too many already available and more will keep coming until someone gets it right. So our web tier for the forseeable future will now consist of WebWork 2.2 + Freemarker + SiteMesh.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-5953994240324411224?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/5953994240324411224/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=5953994240324411224' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/5953994240324411224'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/5953994240324411224'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2005/10/java-web-framework-changes.html' title='Java Web Framework Changes...'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3956414281955169290.post-5488823552031887811</id><published>2005-08-17T17:40:00.000-07:00</published><updated>2008-01-20T16:44:15.326-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><title type='text'>Is Java Dead?</title><content type='html'>I attended &lt;a href="http://meagle.com/articles/page/www.ajug.org"&gt;AJUG&lt;/a&gt; last night and watched Bruce Tate's presentation about 'Stretching Java'. I have to say that this was a great presentation that addressed old and new dynamic languages such as Smalltalk and Ruby. Bruce did a great job explaining the walls that we as Java developers are running up against with Web development today. Maybe Java is not the golden hammer that we thought it was for Web applications. The presentation included demonstrations with the Ruby language, the Seaside framework, and Ruby on Rails. He followed this up how Java developers need to borrow from the strengths of these technologies via techniques such as AOP, metadata/annotations, and incorporating dynamic languages (such as Jython and JRuby) that can run on a JVM. Also, continuations were covered with an example using the Ruby language. This progressed into applying the concept to handle web flows and avoiding back button issues in the browser. Rails and Seaside have native support for continuations via dynamic languages while we need to be more creative and hackish on the Java side to mimic the concept.&lt;br /&gt;&lt;br /&gt;I have already been exposed to Ruby on Rails and I am currently reading the new Pragmatic Programmers book - 'Agile Web Development with Rails'. I am finding that this book is a good read that walks a developer through the process. It is hard to ignore technologies that are as productive as Rails when compared to what I consider a 'lightweight' Java stack to build equivalent Web applications. I consider Struts, Velocity, Spring, and Hibernate a lightweight and productive Java stack for building an end-to-end Web application. However, Web development Ruby on Rails is much faster and elegant compared to any Java stack today.&lt;br /&gt;&lt;br /&gt;What is even more frustrating is when I read about technologies JSF. Let's see - we will build a framework (and by the way make it a standard) which is *way* fatter than something like Struts and tell developers that feeding a framework is a good thing. Rails is so much more simple and elegant that JSF from a presentation/MVC perspective. I think that Sun is making a big mistake with this framework. They finally figured out that EJB was bloated and POJOs were good. Now they are going to learn another lesson on the front end. Well, I am not following the piper this time... More than likely I will stick with my lightweight Java stack - Struts, Velocity, Spring, and Hibernate. If I need to do component development in my presentation layer I will choose Tapestry 4.&lt;br /&gt;&lt;br /&gt;Finally, towards the end of Bruce's presentation he discussed how 2/3 of his development in mid-2006 will be non-Java. If that is not enough to make you take notice of what is going on around you then I do not know what it would take. How much more can we stretch the Java language before we turn to dynamic languages? Is Java dead or dying a slow death?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3956414281955169290-5488823552031887811?l=meagleblog.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://meagleblog.blogspot.com/feeds/5488823552031887811/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3956414281955169290&amp;postID=5488823552031887811' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/5488823552031887811'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3956414281955169290/posts/default/5488823552031887811'/><link rel='alternate' type='text/html' href='http://meagleblog.blogspot.com/2005/08/is-java-dead.html' title='Is Java Dead?'/><author><name>meagle</name><uri>http://www.blogger.com/profile/00192961206104846071</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
