<?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'><id>tag:blogger.com,1999:blog-22146533</id><updated>2009-10-08T18:45:37.238-04:00</updated><title type='text'>blog.sanium.net</title><subtitle type='html'>a software engineering weblog&lt;br&gt;
developing software with unfamiliar tools</subtitle><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/index.htm'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default?start-index=26&amp;max-results=25'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://sanium.net/blog/atom.xml'/><author><name>adam</name><uri>http://www.blogger.com/profile/11299221547922099901</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>27</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-22146533.post-4519106271155280933</id><published>2009-10-08T18:40:00.005-04:00</published><updated>2009-10-08T18:45:37.246-04:00</updated><title type='text'>Drupal's Website Down Error</title><content type='html'>If you get this error from Drupal:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;em&gt;Unable to use the MySQLi database because the MySQLi extension for PHP is not installed. Check your &lt;code&gt;php.ini&lt;/code&gt; to see how you can enable it.&lt;/em&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The php.ini is located at: &lt;code&gt;/etc/php5/apache2/php.ini&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;I was trying to give a direct link to php modules for one of the Drupal modules and it just didn't work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-4519106271155280933?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/4519106271155280933/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=4519106271155280933' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/4519106271155280933'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/4519106271155280933'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2009/10/drupals-website-down-error.html' title='Drupal&apos;s Website Down Error'/><author><name>Anthony Anh Quoc Doan</name><uri>http://www.blogger.com/profile/11030438460565656064</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='00476183566980988244'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-8097841539222197984</id><published>2009-09-20T16:42:00.008-04:00</published><updated>2009-09-20T17:32:26.844-04:00</updated><title type='text'>Drupal 6 is SLOW</title><content type='html'>So Drupal 6 is incredibly slow right now. We, Adam and I, just installed a lot of modules and enabled them.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Setup is Debian 5 with Apache 2&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1st assumption&lt;/span&gt;&lt;br /&gt;Database. Accessing the database could be the reason. Checking the logs (@ /var/log) of mysql reveals nothing, there is nothing there. Perhaps I didn't enable logging. I'll check on that later.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2nd assumption &lt;/span&gt;&lt;br /&gt;Modules. Let's disables all the modules! It helped just a tiny bit. In the process of this I disable all caching options of Drupal.&lt;br /&gt;&lt;br /&gt;So I took a day or so break to think it over, maybe I'm over thinking this. Check on the website again and it's fast! Except, when you click on the administration URL it's very slow. Here's the reason why on the Drupal support forum:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;span style="font-style: italic;"&gt;"&lt;/span&gt;&lt;a style="font-style: italic;" href="http://drupal.org/user/33697" title="View user profile."&gt;cog.rusty&lt;/a&gt;&lt;span style="font-style: italic;"&gt; - February 21, 2009 - 13:50&lt;/span&gt;     &lt;div class="content"&gt;&lt;p style="font-style: italic;"&gt;The admin pages in Drupal 6 are slow because they do perform a lot of housekeeping and rebuilding (thousands of queries, as you noticed, and these are not just cosmetic). Caching probably won't help with that.&lt;/p&gt; &lt;p style="font-style: italic;"&gt;If it is unworkable, disabling the update status module will probably help a bit, and also there are open issues for reducing the number of queries in next updated, but don't expect the D6 admin pages to become snappy. They take the hit for some tasks which make the user part of the site more reliable.&lt;/p&gt; &lt;p&gt;&lt;span style="font-style: italic;"&gt;About the normal site pages... the usual optimization methods should apply." &lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div class="content"&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;Alrighty then, let's just optimize the webpage.&lt;br /&gt;&lt;br /&gt;Installed some tools to help me:&lt;br /&gt;&lt;a href="https://addons.mozilla.org/extensions/moreinfo.php?application=firefox&amp;amp;category=Developer%20Tools&amp;amp;numpg=10&amp;amp;id=1743" rel="nofollow"&gt;Life-of-Request Info extension&lt;/a&gt;&lt;br /&gt;&lt;a href="http://drupal.org/project/devel" rel="nofollow"&gt;Devel module&lt;/a&gt; (where is this log file?)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1st optimization:&lt;/span&gt;&lt;br /&gt;Let's optimize PHP by installing the APC package (read  about it &lt;a href="http://pecl.php.net/package/APC"&gt;here&lt;/a&gt;):&lt;br /&gt;Here's a tutorial on using APC.&lt;br /&gt;http://www.easywebdns.com/tutorials/Linux/php/APC_ON_DEBIAN_LENNY&lt;br /&gt;&lt;br /&gt;Access to main drupal as webmaster:&lt;br /&gt;1st try: 1.3 s&lt;br /&gt;2nd try (browser's cache cleared): .661s&lt;br /&gt;3rd try: 0.877s&lt;br /&gt;&lt;br /&gt;As a non user: 0.536s&lt;br /&gt;&lt;span style="font-weight: bold; font-style: italic;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Let's enable all the modules:&lt;br /&gt;&lt;br /&gt;main page: 1.114s - 1.124s&lt;br /&gt;content generated pages: ~.224s at most &lt; .5s  (no pictures or anything heavy just texts)&lt;br /&gt;&lt;br /&gt;Yay, it's faster!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-8097841539222197984?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/8097841539222197984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=8097841539222197984' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/8097841539222197984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/8097841539222197984'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2009/09/drupal-6-is-slow.html' title='Drupal 6 is SLOW'/><author><name>Anthony Anh Quoc Doan</name><uri>http://www.blogger.com/profile/11030438460565656064</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='00476183566980988244'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-5605781340216492399</id><published>2008-09-03T03:44:00.004-04:00</published><updated>2008-09-03T03:59:50.396-04:00</updated><title type='text'>Setting up Debian Virtual Private Server</title><content type='html'>I recently went through the joy of setting my new virtual private server. Being new to server administration, it's been quite an adventure (and quite a bit of frustration).&lt;br /&gt;&lt;br /&gt;I've learned quite a few lessons on setting up various things and I'll share my experience over the next few entries as I progress and learn more. On my checklist of things (some are done, some are not), I have:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Firewall - iptables&lt;/li&gt;&lt;li&gt;SSH - sshd&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Webserver - Apache2&lt;/li&gt;&lt;li&gt;FTP server - ProFTPD&lt;/li&gt;&lt;li&gt;DNS entries&lt;/li&gt;&lt;li&gt;Mail server (Virtual Mailboxes, Webmail)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Various Languages - Python, PHP, PERL, etc...&lt;/li&gt;&lt;li&gt;Various Apps - emacs, git, dvtm, etc...&lt;/li&gt;&lt;/ul&gt;My server is running Debian Etch.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-5605781340216492399?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/5605781340216492399/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=5605781340216492399' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/5605781340216492399'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/5605781340216492399'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2008/09/setting-up-debian-virtual-private.html' title='Setting up Debian Virtual Private Server'/><author><name>adam</name><uri>http://www.blogger.com/profile/11299221547922099901</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17953937139262306140'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-115886765835981043</id><published>2006-09-21T15:36:00.001-04:00</published><updated>2009-01-23T00:55:14.046-05:00</updated><title type='text'>Starting Lisp, Installing SLIME (on windows)</title><content type='html'>First things first, getting a lisp development environment set up.  After looking around the web for a while, I arrived on SLIME: The Superior Lisp Interaction Mode for Emacs. Installing everything’s pretty simple, mostly because there’s really nothing too much to install.&lt;br /&gt;&lt;br /&gt;I’m using &lt;a href="http://clisp.cons.org/" target="_blank"&gt;GNU Clisp 2.39&lt;/a&gt;, &lt;a href="http://www.gnu.org/software/emacs/" target="_blank"&gt;GNU Emacs 21.3&lt;/a&gt; and &lt;a href="http://common-lisp.net/project/slime/" target="_blank"&gt;SLIME 2.0&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Simply unzip these where ever you want, in my case I put them all in c:\lisp&lt;br /&gt;At this point, if you read the helpful README file,  you’ll be told to put the following code into &lt;code&gt;~/.emacs&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   (add-to-list 'load-path "~/hacking/lisp/slime/")  ; your SLIME directory&lt;br /&gt;   (setq inferior-lisp-program "/opt/sbcl/bin/sbcl") ; your Lisp system&lt;br /&gt;   (require 'slime)&lt;br /&gt;   (slime-setup)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The only problem is, where is this &lt;code&gt;.emacs&lt;/code&gt; file? And what slashes do we use in windows?&lt;br /&gt;The answer to the first question is trivial, let emacs find it for you. The second, use forward slashes.&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;run “runemacs.exe”&lt;/li&gt;&lt;br /&gt;&lt;li&gt;use the &lt;code&gt;C-x C-f&lt;/code&gt; (&lt;code&gt;ctrl+x&lt;/code&gt;, then &lt;code&gt;ctrl+f&lt;/code&gt;) command to open &lt;code&gt;~/.emacs&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;copy and paste the above into it (&lt;code&gt;C-y&lt;/code&gt; is equivalent to pasting in emacs)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;replace the default paths with your own paths, in my case, they are: &lt;code&gt;c:/Lisp/slime-2.0&lt;/code&gt; and &lt;code&gt;c:/Lisp/clisp-239/clisp.exe&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;save the file using &lt;code&gt;C-x C-s&lt;/code&gt;.  (it turns out the file is created at c:\.emacs)&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Now that you have everything set up, restart emacs (I’m not sure if this is required, I just restarted it) and start slime using &lt;code&gt;M-x slime&lt;/code&gt; (&lt;code&gt;alt+x&lt;/code&gt;, then type "&lt;code&gt;slime&lt;/code&gt;"), if everything’s successful, you should see a Lisp interpreter console waiting for you.&lt;br /&gt;&lt;br /&gt;As for me, I’m going through the (free online) book &lt;a href="http://www.blogger.com/%20http://www.gigamonkeys.com/book/" target="_blank"&gt;Practical Common Lisp&lt;/a&gt; by Peter Seibel.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-115886765835981043?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/115886765835981043'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/115886765835981043'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/09/starting-lisp-installing-slime-on.html' title='Starting Lisp, Installing SLIME (on windows)'/><author><name>adam</name><uri>http://www.blogger.com/profile/11299221547922099901</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17953937139262306140'/></author></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-115864292552457561</id><published>2006-09-19T00:56:00.000-04:00</published><updated>2006-09-19T01:53:05.606-04:00</updated><title type='text'>Giving up (on) Java</title><content type='html'>Creating a web application in Java was fun, for a little while. Then the novelty of frameworks like Hibernate wore off, and I found myself programming the same functions in different Managers - over, and over again. It got tedious.&lt;br /&gt;&lt;br /&gt;Maybe I don’t really understand how to use the frameworks properly, or maybe I didn’t choose the right framework, but I feel that there is something I need from this tool that it isn’t provided for me. I am looking for a different language.  From my readings, Lisp seems like it may just fit my needs.&lt;br /&gt;&lt;br /&gt;Lisp is described as a “programmable programming language”. I’m not entirely sure what this means, but it sure does sound exciting. Another exciting thing is that a lot of really smart people seem to be using it and promoting it as a very productive language. (Not just Lisp people, but also people who have many years of experience with modern languages like C/C++/C#/Java).&lt;br /&gt;&lt;br /&gt;The primary problem I see with Java is that it seems as if it wasn’t designed for a single person to create complicated applications. All the frameworks and segmentation of code make it the perfect language for a bunch of programmers to build large systems in. This is not what I’m looking for now, so I’m going to keep looking for a more suitable tool to build what I need.&lt;br /&gt;&lt;br /&gt;(there are a myriad of sites describing Lisp to different degrees, just search for it)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-115864292552457561?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/115864292552457561/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=115864292552457561' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/115864292552457561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/115864292552457561'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/09/giving-up-on-java.html' title='Giving up (on) Java'/><author><name>adam</name><uri>http://www.blogger.com/profile/11299221547922099901</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17953937139262306140'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-114478841927348688</id><published>2006-04-11T16:42:00.000-04:00</published><updated>2006-04-11T19:04:01.216-04:00</updated><title type='text'>Ant scripting with Groovy</title><content type='html'>I've been playing a lot with Ant lately, and while it's a great tool - I like to think of it as the duct tape of Java programming - the restrictions of its XML format can make it a pain to use it for more complicated things.  Yes, you can always write an Ant task to do what you need, but that has its disadvantages too - you need to worry about imports, compiling it, adding the jar to the classpath, adding a taskdef... a lot of work for what should be a simple scripting operation.&lt;br /&gt;&lt;br /&gt;One possible solution - the &lt;a href="http://groovy.codehaus.org/Groovy+Ant+Task"&gt;Groovy Ant Task&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Here's a very simple example of the power Groovy brings to Ant.  Let's take your standard Ant echo task:&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;echo&lt;/span&gt; &lt;span class="attr"&gt;message&lt;/span&gt;&lt;span class="kwrd"&gt;="Ant is great"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;There are several ways to write this using the Groovy task:&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;groovy&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  ant.echo(message:"Groovy is great")&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;groovy&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;or&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;groovy&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  ant.echo("Groovy is great")&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;groovy&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;or even&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;groovy&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  println("Groovy is great")&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;groovy&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;This might not seem like it's worth the trouble... but keep in mind that the string "Groovy is great" can be treated as a Java string for all intensive purposes... and manipulated with any of the Java methods.  Not exactly trivial to do that using only Ant.&lt;br /&gt;&lt;br /&gt;Let's try something more complex.  I need to apply a XSL transform to all the XML files in a folder (quite achievable with Ant tasks) and create a simple HTML file with links to all the transformed files (not that easy using Ant).  This is part of a &lt;a href="http://webtest.canoo.com"&gt;Canoo Webtest&lt;/a&gt; Ant runner script, so ignore any of the specifics.&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&amp;lt;groovy&amp;gt;&lt;br /&gt;    def scanner = ant.fileScanner {&lt;br /&gt;        fileset(dir:&lt;span class="kwrd"&gt;new&lt;/span&gt; File(properties.get(&lt;span class="str"&gt;'webtest.config.resultpath'&lt;/span&gt;))) {&lt;br /&gt;            include(name:&lt;span class="str"&gt;'**/*.xml'&lt;/span&gt;)&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    resultFile = &lt;span class="kwrd"&gt;new&lt;/span&gt; File(properties.get(&lt;span class="str"&gt;'webtest.config.resultpath'&lt;/span&gt;) + &lt;span class="str"&gt;"/results.html"&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;    resultFile.write(&lt;span class="str"&gt;"&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;Canoo Webtest results&amp;lt;/h1&amp;gt;&amp;lt;ul&amp;gt;"&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;for&lt;/span&gt; (f &lt;span class="kwrd"&gt;in&lt;/span&gt; scanner) {&lt;br /&gt;        ant.echo(message:f.name){}&lt;br /&gt;        fileName = (f.name =~ &lt;span class="str"&gt;"xml"&lt;/span&gt;).replaceAll(&lt;span class="str"&gt;"html"&lt;/span&gt;);&lt;br /&gt;        resultFile.append(&lt;span class="str"&gt;'&amp;lt;li&amp;gt;&amp;lt;a href="'&lt;/span&gt;+fileName+&lt;span class="str"&gt;'"&amp;gt;'&lt;/span&gt;+fileName+&lt;span class="str"&gt;'&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;'&lt;/span&gt;);&lt;br /&gt;        ant.style(basedir:properties.get(&lt;span class="str"&gt;'webtest.config.resultpath'&lt;/span&gt;),&lt;br /&gt;              destdir:properties.get(&lt;span class="str"&gt;'webtest.config.resultpath'&lt;/span&gt;),&lt;br /&gt;              includes:f.name,&lt;br /&gt;              force:&lt;span class="str"&gt;"true"&lt;/span&gt;,&lt;br /&gt;              extension:&lt;span class="str"&gt;".html"&lt;/span&gt;,&lt;br /&gt;              style:properties.get(&lt;span class="str"&gt;'webtest.config.resultpath'&lt;/span&gt;)+&lt;span class="str"&gt;"/WebTestReport.xsl"&lt;/span&gt;)&lt;br /&gt;          {&lt;br /&gt;            ant.param(name:&lt;span class="str"&gt;"reporttime"&lt;/span&gt;, expression:properties.get(&lt;span class="str"&gt;'report.time'&lt;/span&gt;));&lt;br /&gt;            ant.param(name:&lt;span class="str"&gt;"title"&lt;/span&gt;, expression:properties.get(&lt;span class="str"&gt;'ant.project.name'&lt;/span&gt;));&lt;br /&gt;          }&lt;br /&gt;    resultFile.append(&lt;span class="str"&gt;"&amp;lt;/ul&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;"&lt;/span&gt;);&lt;br /&gt;    }&lt;br /&gt;&amp;lt;/groovy&amp;gt;&lt;/pre&gt;&lt;br /&gt;Not TOO bad, hopefully.  Let's break it down into parts:&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;    def scanner = ant.fileScanner {&lt;br /&gt;        fileset(dir:&lt;span class="kwrd"&gt;new&lt;/span&gt; File(properties.get(&lt;span class="str"&gt;'webtest.config.resultpath'&lt;/span&gt;))) {&lt;br /&gt;            include(name:&lt;span class="str"&gt;'**/*.xml'&lt;/span&gt;)&lt;br /&gt;        }&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;Nothing that couldn't be done using Ant here, this is just the fileScanner task.  One thing worth noting is &lt;code&gt;properties.get('webtest.config.resultpath')&lt;/code&gt;, which is the equivalent of &lt;code&gt;${webtest.config.resultpath}&lt;/code&gt;.  Next:&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;    resultFile = &lt;span class="kwrd"&gt;new&lt;/span&gt; File(properties.get(&lt;span class="str"&gt;'webtest.config.resultpath'&lt;/span&gt;) + &lt;span class="str"&gt;"/results.html"&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;    resultFile.write(&lt;span class="str"&gt;"&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;Canoo Webtest results&amp;lt;/h1&amp;gt;&amp;lt;ul&amp;gt;"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;This is just Groovy code... which isn't that different from plain Java code.  Create a file and start writing to it.  Again, here we're using the Ant property &lt;code&gt;${webtest.config.resultpath}&lt;/code&gt; which is defined elsewhere in the Ant project.  Next:&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;    &lt;span class="kwrd"&gt;for&lt;/span&gt; (f &lt;span class="kwrd"&gt;in&lt;/span&gt; scanner) {&lt;br /&gt;        ant.echo(message:f.name){}&lt;br /&gt;        fileName = (f.name =~ &lt;span class="str"&gt;"xml"&lt;/span&gt;).replaceAll(&lt;span class="str"&gt;"html"&lt;/span&gt;);&lt;br /&gt;        resultFile.append(&lt;span class="str"&gt;'&amp;lt;li&amp;gt;&amp;lt;a href="'&lt;/span&gt;+fileName+&lt;span class="str"&gt;'"&amp;gt;'&lt;/span&gt;+fileName+&lt;span class="str"&gt;'&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;'&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;Again, mostly just Groovy code. Iterate through all the files and add a link to them in the html file we're building. The fun part is &lt;code&gt;fileName = (f.name =~ "xml").replaceAll("html");&lt;/code&gt; - a simple regular expression to replace the xml extension in the file name to html. Lastly:&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;        ant.style(basedir:properties.get(&lt;span class="str"&gt;'webtest.config.resultpath'&lt;/span&gt;),&lt;br /&gt;              destdir:properties.get(&lt;span class="str"&gt;'webtest.config.resultpath'&lt;/span&gt;),&lt;br /&gt;              includes:f.name,&lt;br /&gt;              force:&lt;span class="str"&gt;"true"&lt;/span&gt;,&lt;br /&gt;              extension:&lt;span class="str"&gt;".html"&lt;/span&gt;,&lt;br /&gt;              style:properties.get(&lt;span class="str"&gt;'webtest.config.resultpath'&lt;/span&gt;)+&lt;span class="str"&gt;"/WebTestReport.xsl"&lt;/span&gt;)&lt;br /&gt;          {&lt;br /&gt;            ant.param(name:&lt;span class="str"&gt;"reporttime"&lt;/span&gt;, expression:properties.get(&lt;span class="str"&gt;'report.time'&lt;/span&gt;));&lt;br /&gt;            ant.param(name:&lt;span class="str"&gt;"title"&lt;/span&gt;, expression:properties.get(&lt;span class="str"&gt;'ant.project.name'&lt;/span&gt;));&lt;br /&gt;          }&lt;/pre&gt;&lt;br /&gt;This is a straightforward translation of the Ant style task into Groovy syntax.&lt;br /&gt;&lt;br /&gt;Just one step left.  Because of all the &amp;lt; &amp;gt; signs in there, I couldn't figure out how to include this directly in the Ant file.  So I saved it as &lt;code&gt;formatresults.groovy&lt;/code&gt;, and called it with:&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;        &amp;lt;groovy src=&lt;span class="str"&gt;"${resources.dir}/formatresults.groovy"&lt;/span&gt;/&amp;gt;&lt;/pre&gt;&lt;br /&gt;That wasn't so bad!  The Groovy code isn't really any harder to read than the Ant syntax; it doesn't need to be compiled, so the source is simply stored alongside the Ant file, and modifications aren't any more difficult than changing the Ant script itself; and best of all, it allows much greater flexibility than the traditional Ant format.&lt;br /&gt;&lt;br /&gt;So far I've used snippets of Groovy code inside an existing Ant script.  As the Groovy site shows, it's quite possible to go to the logical extreme and &lt;a href="http://groovy.codehaus.org/Ant+Scripting"&gt;write the entire Ant script as Groovy code&lt;/a&gt; - or have a Groovy script with calls to Ant tasks in it, really.&lt;br /&gt;&lt;br /&gt;I guess the above is just a really longwinded way to say: if you're using Ant for anything more complex than a simple build script, take the half an hour to set up and play with Groovy a bit.  It's well worth it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-114478841927348688?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/114478841927348688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=114478841927348688' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114478841927348688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114478841927348688'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/04/ant-scripting-with-groovy.html' title='Ant scripting with Groovy'/><author><name>Alex</name><uri>http://www.blogger.com/profile/05017624895316103348</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='12320622651016527192'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-114382343450389096</id><published>2006-03-31T11:40:00.000-05:00</published><updated>2006-03-31T11:46:19.023-05:00</updated><title type='text'>Multiple Values in a Single Valued Form Field</title><content type='html'>There are times when you wish to have a single valued form field pass multiple values. For example, maybe you want a radio button to pass in a height and a width value at the same time. It wouldn’t be too hard to just pass in a comma separated list of values in predefined order and then parse them in the backend, but that would be very error prone and not too elegant.  Another approach is to use something similar to the &lt;code&gt;http GET request&lt;/code&gt; where you use &lt;code&gt;parameter1=value1&amp;parameter2=value2&lt;/code&gt; style and then extract the values in the backend.  This approach also involves some ugly string manipulation.&lt;br /&gt;&lt;br /&gt;Here’s an example from work:&lt;br /&gt;&lt;br /&gt;The existing application passes in a &lt;code&gt;customer_order_id&lt;/code&gt; to the backend which is specific to a customer. We wish to extend it to also handle generic orders which are identified as &lt;code&gt;order_id&lt;/code&gt;. How do we identify if the value passed in is a &lt;code&gt;customer_order_id&lt;/code&gt; or an &lt;code&gt;order_id&lt;/code&gt;? Having already rejected the two possible solutions that I’ve outlined above, I thought about perhaps having negative / positive numbers to distinguish between the two, but that’s way too hacker-ish.&lt;br /&gt;&lt;br /&gt;The solution I arrived at involves using multiple hidden fields and referencing them from the single valued for object:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;ol&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;li&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;=”&lt;span class="attr"&gt;radio&lt;/span&gt;” &lt;span class="attr"&gt;value&lt;/span&gt;=”&lt;span class="attr"&gt;1&lt;/span&gt;”&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; value 1&lt;br /&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;=”&lt;span class="attr"&gt;hidden&lt;/span&gt;” &lt;span class="attr"&gt;value&lt;/span&gt;=” &lt;span class="attr"&gt;order_id-1&lt;/span&gt;” &lt;span class="attr"&gt;value&lt;/span&gt;=”&lt;span class="attr"&gt;generic-order-1&lt;/span&gt;”&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;li&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;li&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;=”&lt;span class="attr"&gt;radio&lt;/span&gt;” &lt;span class="attr"&gt;value&lt;/span&gt;=”&lt;span class="attr"&gt;2&lt;/span&gt;”&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; value 2&lt;br /&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;=”&lt;span class="attr"&gt;hidden&lt;/span&gt;” &lt;span class="attr"&gt;value&lt;/span&gt;=”&lt;span class="attr"&gt;order_id-2&lt;/span&gt;” &lt;span class="attr"&gt;value&lt;/span&gt;=”&lt;span class="attr"&gt;generic-order-2&lt;/span&gt;”&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;=”&lt;span class="attr"&gt;hidden&lt;/span&gt;” &lt;span class="attr"&gt;value&lt;/span&gt;=”&lt;span class="attr"&gt;customer_order_id-2&lt;/span&gt;” &lt;span class="attr"&gt;value&lt;/span&gt;=”&lt;span class="attr"&gt;customer-order-2&lt;/span&gt;”&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;li&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;li&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;=”&lt;span class="attr"&gt;radio&lt;/span&gt;” &lt;span class="attr"&gt;value&lt;/span&gt;=”&lt;span class="attr"&gt;3&lt;/span&gt;”&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; value 3&lt;br /&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;=”&lt;span class="attr"&gt;hidden&lt;/span&gt;” &lt;span class="attr"&gt;value&lt;/span&gt;=”&lt;span class="attr"&gt;customer_order_id-3&lt;/span&gt;” &lt;span class="attr"&gt;value&lt;/span&gt;=”&lt;span class="attr"&gt;customer-order-3&lt;/span&gt;”&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;li&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;ol&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then, in the backend, I only have to check the corresponding hidden field for the value.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-114382343450389096?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/114382343450389096/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=114382343450389096' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114382343450389096'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114382343450389096'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/03/multiple-values-in-single-valued-form.html' title='Multiple Values in a Single Valued Form Field'/><author><name>adam</name><uri>http://www.blogger.com/profile/11299221547922099901</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17953937139262306140'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-114291030411163939</id><published>2006-03-20T22:02:00.000-05:00</published><updated>2006-03-20T22:44:15.250-05:00</updated><title type='text'>Struts, Tiles and Validator</title><content type='html'>Just a bit of background for my next few entries: At work, I've been designing the front end structure for some new pages that we are introducing. We have decided to start using some new technology (for us), namely Tiles and Validator. Since the existing framework is using Struts 1.1, I am confined to using earlier versions of Tiles and Validator. For the new designs, I have kept several goals in mind:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Separation of business and view&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Reuse of components&lt;br /&gt;&lt;/li&gt;&lt;li&gt;More configuration, less hard coding&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;First things first, creating a stand alone application to play around with the various technologies: Struts, Tiles, Validator, JSTL.  I am using the &lt;a href="http://archive.apache.org/dist/struts/struts-1.1/"&gt;Struts 1.1 package&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;h3&gt;The basic web app&lt;/h3&gt;&lt;p&gt;First things first, creating a basic webapp to play around with.  For this step, I started out by using the struts-blank .war file included in the struts package (&lt;code&gt;/webapps/struts-blank.war&lt;/code&gt;).&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Since getting compiling in eclipse was a bit of a hassle (at least it was for the one included in the 1.1 package), I'll go over it here. unzip the struts-blank.war file into a working folder, start up eclipse, &lt;code&gt;create a New Java Project&lt;/code&gt;, pick a project name (e.g. struts-test), select "Create project from existing source" and using the directory that you expanded your struts-blank into, click on "finish".&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Next, right click on the &lt;code&gt;/WEB-INF/src&lt;/code&gt; folder in the Package Explorer view and select &lt;code&gt;build path &gt; Use as Source Folder&lt;/code&gt;.  For this project, we'll use Ant to build the project, go ahead and show the ant view (&lt;code&gt;Window &gt; Show View &gt; Ant&lt;/code&gt;). You should see an ant view on the right side of your main editor, drag the &lt;code&gt;/WEB-INF/src/build.xml&lt;/code&gt; file into the ant panel.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Using the default build script, if you try to generate a war file using the &lt;code&gt;dist&lt;/code&gt; target, you'll get the following warning: &lt;code&gt;Warning: selected war files include a WEB-INF/web.xml which will be ignored (please use webxml attribute to war task)&lt;/code&gt;, if you then try to deploy the generated war file, the application won't deploy property. To fix this, add an &lt;code&gt;&amp;lt;exclude name="**/web.xml" /&amp;gt;&lt;/code&gt; line in the &lt;code&gt;&amp;lt;war&amp;gt;&lt;/code&gt; task in the &lt;code&gt;dist&lt;/code&gt; target. This will prevent duplicate &lt;code&gt; web.xml&lt;/code&gt; files.&lt;br /&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;h3&gt;How hard is it to get JSTL to work.. Properly!?&lt;/h3&gt;There are four main parts to getting JSTL working, if you leave out one or more of these, like I did, you're page will most likely not work. First, you need both &lt;code&gt;jstl.jar&lt;/code&gt; and &lt;code&gt;standard.jar&lt;/code&gt;, in addition you need to declare the &lt;code&gt;taglib&lt;/code&gt; at the top of any JSP pages that you intend to use JSTL on:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;%@ taglib prefix="c" uri="http://java.sun.com/jspjstl/core" %&amp;gt;&lt;/pre&gt;&lt;br /&gt;or&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %&amp;gt;&lt;/pre&gt;&lt;br /&gt;if you are using an older version. For the other, non-core, JSTL tags, you'll need to add/use their respective taglib declarations.&lt;br /&gt;You can put a taglib declaration in your &lt;code&gt;web.xml&lt;/code&gt; file, but I've got my applications working without them:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;taglib&amp;gt;&lt;br /&gt;&amp;lt;taglib-uri&amp;gt;http://java.sun.com/jstl/core&amp;lt;/taglib-uri&amp;gt;&lt;br /&gt;&amp;lt;taglib-location&amp;gt;/WEB-INF/c.tld&amp;lt;/taglib-location&amp;gt;&lt;br /&gt;&amp;lt;/taglib&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Personally, I like to keep my &lt;code&gt;*.tld&lt;/code&gt; files in their own seperate folder, but the sample struts-blank webapp puts them in the &lt;code&gt;WEB-INF&lt;/code&gt; directory along with the XML configuration files.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;h3&gt;Playing With JSTL&lt;/h3&gt;&lt;p&gt;Now that we finally have JSTL set up, let's experiment and see what it can do.&lt;/p&gt;&lt;p&gt;I started out using a single JSP, the &lt;code&gt;index.jsp&lt;/code&gt; file found in the root project folder. I set up some classes that I wanted to play with inside a java scriptlet at the top of the JSP file:&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&amp;lt;%&lt;br /&gt;class Outer {&lt;br /&gt;private String name;&lt;br /&gt;private java.util.List inners;&lt;br /&gt;&lt;br /&gt;public String getName() {&lt;br /&gt;return name;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public java.util.List getInners() {&lt;br /&gt;return inners;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void setName(String name) {&lt;br /&gt;this.name = name;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void setInners(java.util.List inners) {&lt;br /&gt;this.inners = inners;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class Inner {&lt;br /&gt;private String value;&lt;br /&gt;&lt;br /&gt;public String getValue() {&lt;br /&gt;return value;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void setValue(String value) {&lt;br /&gt;this.value = value;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Outer o1 = new Outer();&lt;br /&gt;o1.setName("outer 1");&lt;br /&gt;&lt;br /&gt;Inner i1 = new Inner();&lt;br /&gt;Inner i2 = new Inner();&lt;br /&gt;Inner i3 = new Inner();&lt;br /&gt;&lt;br /&gt;i1.setValue("inner 1");&lt;br /&gt;i2.setValue("inner 2");&lt;br /&gt;i3.setValue("inner 3");&lt;br /&gt;&lt;br /&gt;java.util.List ins = new java.util.LinkedList();&lt;br /&gt;ins.add(i1);&lt;br /&gt;ins.add(i2);&lt;br /&gt;ins.add(i3);&lt;br /&gt;&lt;br /&gt;o1.setInners(ins);&lt;br /&gt;&lt;br /&gt;pageContext.setAttribute("out1", o1);&lt;br /&gt;pageContext.setAttribute("test", "Testing String");&lt;br /&gt;%&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;I also included some code to set up a few objects. To access the objects, we have to place them into the session in one of the scopes: &lt;code&gt;application&lt;/code&gt;, &lt;code&gt;session&lt;/code&gt;, &lt;code&gt;page&lt;/code&gt; or &lt;code&gt;request&lt;/code&gt;&lt;/p&gt;&lt;p&gt;I'll place my object in the default (&lt;code&gt;page&lt;/code&gt;) scope and try to access it using the &lt;code&gt;JSTL c:out&lt;/code&gt; tag:&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&amp;lt;c:out value="${test}"&amp;gt;&amp;lt;/c:out&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Works fine and displays "Testing String" as we would expect, but:&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&amp;lt;c:out value="${out1.name}"&amp;gt;&amp;lt;/c:out&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;will, instead of accessing the &lt;code&gt;getName()&lt;/code&gt; Javabean method and returning our name, reward us with a &lt;code&gt;JspException&lt;/code&gt;:&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;javax.servlet.ServletException: javax.servlet.jsp.JspException: An error occurred while evaluating custom action attribute "value" with value "${out1.name}": An error occurred while getting property "name" from an instance of class org.apache.jsp.index_jsp$1$Outer (java.lang.IllegalAccessException: Class org.apache.taglibs.standard.lang.jstl.ArraySuffix can not access a member of class org.apache.jsp.index_jsp$1$Outer with modifiers "public")&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;What happens here is the tag tries to access the &lt;code&gt;getName()&lt;/code&gt; method of the class, but cannot because the class is not public. This is easy enough to fix, just extract our two classes into their own Java class files. We then import these classes into our &lt;code&gt;JSP&lt;/code&gt; and our code works as expected:&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&amp;lt;%@page import="com.adamdou.fronts.dummy.Outer"%&amp;gt;&lt;br /&gt;&amp;lt;%@page import="com.adamdou.fronts.dummy.Inner"%&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Next, try doing some fancier things like displaying all the &lt;code&gt;Inner&lt;/code&gt; items in our &lt;code&gt;Outer&lt;/code&gt; item:&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&amp;lt;h2&amp;gt;&amp;lt;c:out value="${out1.name}"&amp;gt;&amp;lt;/c:out&amp;gt;&amp;lt;/h2&amp;gt;&lt;br /&gt;&amp;lt;ol&amp;gt;&lt;br /&gt;&amp;lt;c:forEach var="in" items="${out1.inners}"&amp;gt;&lt;br /&gt;&amp;lt;li&amp;gt;&amp;lt;c:out value="${in.value}" /&amp;gt; &amp;lt;/li&amp;gt;&lt;br /&gt;&amp;lt;/c:forEach&amp;gt;&lt;br /&gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Once you get &lt;code&gt;JSTL&lt;/code&gt; up and running, it's pretty simple to experiment with different things, there's also a plethora of tutorials online such as the &lt;a href="http://www-128.ibm.com/developerworks/java/library/j-jstl0211.html"&gt;IBM JSTL Primer&lt;/a&gt;, a simple online search should turn up quite a few more.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;That's it for now, next time I'll talk about some interesting adventures I've had with the &lt;a href="http://jakarta.apache.org/commons/validator/"&gt;Commons Validator&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-114291030411163939?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/114291030411163939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=114291030411163939' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114291030411163939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114291030411163939'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/03/struts-tiles-and-validator_20.html' title='Struts, Tiles and Validator'/><author><name>adam</name><uri>http://www.blogger.com/profile/11299221547922099901</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17953937139262306140'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-114170107536625536</id><published>2006-03-06T22:09:00.000-05:00</published><updated>2006-03-06T22:20:53.520-05:00</updated><title type='text'>The SUN Website</title><content type='html'>&lt;p&gt;I was starting to write about something technical, but... &lt;/p&gt;&lt;p&gt;I've had a very interesting experience with the &lt;a href="http://java.sun.com"&gt;SUN website&lt;/a&gt; lately.&lt;/p&gt;&lt;p&gt;It all started when I tried to download the JSTL files.&lt;/p&gt;&lt;p&gt;Naturally, I started out by searching for JSTL on Google and was redirect to SUN's official JSTL page, &lt;a href="http://java.sun.com/products/jsp/jstl/"&gt;http://java.sun.com/products/jsp/jstl/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Clicking on the &lt;code&gt;Downloads&lt;/code&gt; link, I find that I can't just download a simple package and be done with it, instead I receive the joy of choosing between several different implementation. I instinctively go for the ones that promise &lt;code&gt;JSTL 1.1&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Selecting the J2EE 1.4 SDK link, I'm appalled to find that there is, again, no simple way to just download a simple package that could be extracted to a &lt;code&gt;j2ee_sdk_1.4_03&lt;/code&gt; folder, but no such luck. All the available download options include some sort of IDE or application server (which is not only useless, considering that I already have 2 application servers that both perform better than the ones from SUN) &lt;a href="http://java.sun.com/j2ee/1.4/download.html"&gt;http://java.sun.com/j2ee/1.4/download.html&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Furthermore, right underneath the first package, are the components of its make up. It eludes me how the &lt;code&gt;Sun Java System Application Server PE 8.2&lt;/code&gt;, &lt;code&gt;JDK 5.0&lt;/code&gt;, &lt;code&gt;Platform API Docs&lt;/code&gt; and &lt;code&gt;Samples Bundle&lt;/code&gt; combine into a &lt;code&gt;J2EE&lt;/code&gt; SDK, much less a &lt;code&gt;J2EE 1.4&lt;/code&gt; SDK&lt;/p&gt;&lt;p&gt;Back to the main JSTL page, I tried getting the &lt;code&gt;Java Web Services Developer Pack (Java WSDP)&lt;/code&gt;. Downloaded and installed it, but alas, there's no mention what-so-ever about JSTL in any of the documentation for the WSDP.&lt;/p&gt;&lt;p&gt;Finally, I decided to give the &lt;code&gt;Jakarta Taglibs&lt;/code&gt; a shot, about 2 minutes later, I have an implementation of JSTL 1.1 in form of the package &lt;code&gt;jakarta-taglibs-standard-1.1.2&lt;/code&gt;. (&lt;a href="http://jakarta.apache.org/site/downloads/downloads_taglibs-standard.cgi"&gt;http://jakarta.apache.org/site/downloads/downloads_taglibs-standard.cgi&lt;/a&gt;)&lt;/p&gt;&lt;p&gt;Admittedly, I should have known to look at the apache jakarta project first, but the SUN site makes it very difficult for newcomers to locate required resources for development.&lt;/p&gt;&lt;p&gt;&amp;lt;/rant&amp;gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-114170107536625536?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/114170107536625536/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=114170107536625536' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114170107536625536'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114170107536625536'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/03/sun-website.html' title='The SUN Website'/><author><name>adam</name><uri>http://www.blogger.com/profile/11299221547922099901</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17953937139262306140'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-114165420260897853</id><published>2006-03-06T08:44:00.000-05:00</published><updated>2006-03-06T09:10:07.010-05:00</updated><title type='text'>Firefox extension development</title><content type='html'>As some of you might know, while Adam's trying to make some sense of Hibernate for our &lt;a href="http://sourceforge.net/projects/duckstein/"&gt;Duckstein project&lt;/a&gt;, I've started playing around with a side project that happens to be a Firefox extension.&lt;br /&gt;&lt;br /&gt;Much to my surprise, the experience so far has been quite enjoyable.  Documentation is quite good - a quick tutorial on how to &lt;a href="http://kb.mozillazine.org/Getting_started_with_extension_development"&gt;set up a Firefox extension for development&lt;/a&gt;, the excellent &lt;a href="http://www.xulplanet.com/tutorials/xultu/"&gt;XUL tutorials&lt;/a&gt; from XULPlanet, and I had a simple tic-tac-toe extension working in under two hours.  Most of those two hours, as usual, were spent on silly mistakes - and a lot of it could've been avoided if I remembered to use the &lt;a href="http://forums.mozillazine.org/viewtopic.php?t=318102"&gt;error console&lt;/a&gt; earlier (or if I knew JavaScript properly in the first place).&lt;br /&gt;&lt;br /&gt;It seems pretty simple to modify any pages Firefox loads before they're displayed.  The fun part of the extension, however, is to interact with pages that are updated dynamically after they're loaded (like, maybe, by XMLHttpRequest, hmm?)  This has proved to be somewhat more difficult.  I can think of two general approaches... one is to set up an event listener that notifies the extension whenever an AJAX request is sent/received or when the page is modified... the other is to "simply" add my own javascript code to the existing JS code on the page that handles the requests.  The first solution seems (in theory) simpler, while the second seems more elegant... I'm not sure which one would be more robust (e.g. resistant to changes made to the site).   So far the first solution has been a dead end, however... it seems that it should be enough to add a DOMSubtreeModified listener to the element I'm interested in watching - but I can't even figure out if Firefox supports this listener.&lt;br /&gt;&lt;br /&gt;I've also come across a few pretty cool extensions.  The first is &lt;a href="https://addons.mozilla.org/extensions/moreinfo.php?id=1843&amp;application=firefox"&gt;Firebug&lt;/a&gt; (&lt;a href="http://www.joehewitt.com/software/firebug/"&gt;home page&lt;/a&gt;), an excellent error/debug console for Firefox that also supports XMLHttpRequests - whenever I'll work on an AJAX project this will be invaluable, and the perfect complement to the also-excellent &lt;a href="https://addons.mozilla.org/extensions/moreinfo.php?application=firefox&amp;amp;category=Developer%20Tools&amp;numpg=10&amp;amp;id=60"&gt;Web Developer&lt;/a&gt; extension which I've been using for a while already.  Another is &lt;a href="http://www.customizegoogle.com/"&gt;Customize Google&lt;/a&gt; - should be self-explanatory.  Looking at the source code for these two should help me figure out how to get my extension working, as well.&lt;br /&gt;&lt;br /&gt;The third neat extension is &lt;a href="http://firefoxit.mozdev.org/"&gt;Firefoxit &lt;/a&gt;- a sort of desktop-inside-Firefox that can hold widgets.  Fun stuff.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-114165420260897853?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/114165420260897853/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=114165420260897853' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114165420260897853'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114165420260897853'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/03/firefox-extension-development.html' title='Firefox extension development'/><author><name>Alex</name><uri>http://www.blogger.com/profile/05017624895316103348</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='12320622651016527192'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-114099000113310754</id><published>2006-02-26T16:16:00.000-05:00</published><updated>2006-02-26T16:40:01.183-05:00</updated><title type='text'>Tomcat &amp; Tapestry</title><content type='html'>One thing that has been frustrating over the past few months - both for &lt;a href="http://sourceforge.net/projects/duckstein/"&gt;Project Duckstein&lt;/a&gt; and at work - is that I still don't know an "elegant" way to reliably redeploy a WAR file in Tomcat.   Simply replacing the WAR in the webapps folder doesn't usually work... neither does undeploying/redeploying through the Tomcat manager.  Sometimes it works, sometimes it doesn't... more often than not the only reliable way is to stop Tomcat, delete the WAR and the exploded folder, compile the new WAR, and start Tomcat.  Even the ant tasks to stop/start Tomcat don't seem to work, because... well, I forget why, but they don't.&lt;br /&gt;&lt;br /&gt;The very kludgy way I have right now is to use ant to stop/start the Tomcat service itself:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;target&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="tomcat-netstart"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;exec&lt;/span&gt; &lt;span class="attr"&gt;executable&lt;/span&gt;&lt;span class="kwrd"&gt;="net"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;arg&lt;/span&gt; &lt;span class="attr"&gt;line&lt;/span&gt;&lt;span class="kwrd"&gt;="start tomcat5"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;exec&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;target&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;target&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="tomcat-netstop"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;exec&lt;/span&gt; &lt;span class="attr"&gt;executable&lt;/span&gt;&lt;span class="kwrd"&gt;="net"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;arg&lt;/span&gt; &lt;span class="attr"&gt;line&lt;/span&gt;&lt;span class="kwrd"&gt;="stop tomcat5"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;exec&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;target&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The target for a full deployment, then, is&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;target&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="full"&lt;/span&gt; &lt;span class="attr"&gt;description&lt;/span&gt;&lt;span class="kwrd"&gt;="Full deployment"&lt;/span&gt; &lt;span class="attr"&gt;depends&lt;/span&gt;&lt;span class="kwrd"&gt;="tomcat-netstop,clean,dist,tomcat-netstart"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Not exactly the most elegant solution... we still need to figure out a better way to do this.&lt;br /&gt;&lt;br /&gt;Now, for development, this is even worse.  The above mentioned process takes up to 30 seconds on my (fairly old) computer... plus another 10 seconds or so for Tapestry to first load the pages.  Trial-and-error coding thus becomes a very, very painful process.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://spindle.sourceforge.net/"&gt;Spindle Eclipse plugin&lt;/a&gt; is supposed to alleviate this, along with goodies like syntax highlighting, auto-completion, and other stuff that makes the whole process more sane... unfortunately Spindle is not yet available for Tapestry 4.  I did, however, notice a &lt;a href="http://www.jroller.com/page/glongman?entry=eclipse_wtp_and_tapestry_4"&gt;post on the Spindle blog&lt;/a&gt; linking to instructions for &lt;a href="http://wiki.apache.org/jakarta-tapestry/HowToSetupEclipseWtp"&gt;setting up the Eclipse Web Tools Platform&lt;/a&gt; to work with Tapestry.  The largest benefit at this stage of development is, by far, the automatic deployment of any changes made to classes or resources.  That's right, no server restart... just save the changes and see them take effect right away.  This is how I have my Eclipse set up at work, and I was really missing this capability while working on Duckstein so far.&lt;br /&gt;&lt;br /&gt;So I am now &lt;a href="http://www.eclipse.org/webtools/releases/1.0/"&gt;downloading the WTP&lt;/a&gt;... let's see how well it works.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-114099000113310754?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/114099000113310754/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=114099000113310754' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114099000113310754'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114099000113310754'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/tomcat-tapestry.html' title='Tomcat &amp; Tapestry'/><author><name>Alex</name><uri>http://www.blogger.com/profile/05017624895316103348</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='12320622651016527192'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-114074111819856486</id><published>2006-02-23T19:31:00.000-05:00</published><updated>2006-02-23T20:13:27.143-05:00</updated><title type='text'>Floating Forms with CSS</title><content type='html'>More than likely in any medium or large web application will have a page or two filled with form input boxes.&lt;br /&gt;&lt;br /&gt;I’ve just had the pleasure of creating several of these pages.&lt;br /&gt;&lt;br /&gt;Now, standard practices was to create a table and stick the labels and field into it. While this is a satisfactory solution, it suffers from several issues: it doesn’t allow you to easily more the fields around and it doesn’t work when you don’t want the fields to all line up.&lt;br /&gt;&lt;br /&gt;A good solution that address these issues is the floating form fields.  Here’s a small sample of what the form should look like:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://sanium.net/blog/uploaded_images/sample1-752560.png" alt="" /&gt;&lt;br /&gt;&lt;br /&gt;here's the HTML code for it:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;/style&amp;gt;&lt;br /&gt;&amp;lt;div style=”background-color:#eee; width: 400px;”&amp;gt;&lt;br /&gt;&amp;lt;div class=”field”&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt;First Name&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;input type=”text” size=”20”&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div class=”field”&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt;Last Name&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;input type=”text” size=”20”&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div class=”field”&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt;Email&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;input type=”text” size=”30”&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div style=”clear:both”&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div class=”field”&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt;Phone&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;input type=”text” size=”16”&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div class=”field”&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt;City&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;input type=”text” size=”15”&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div class=”field”&amp;gt;&lt;br /&gt;&amp;lt;p&amp;gt;Postal / Zip Code&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;input type=”text” size=”6”&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div style=”clear:both”&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and CSS code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;body {&lt;br /&gt; font-family: verdana;&lt;br /&gt; font-size: 12px;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.field {&lt;br /&gt; float:left;&lt;br /&gt; padding: 5px;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;input {&lt;br /&gt; border:1px solid #aaa;&lt;br /&gt; font: 12px;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;p {&lt;br /&gt; margin: 5px;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Basically, what’s happening here is that each label along with its field is put into a &lt;code&gt;div&lt;/code&gt; layer and then &lt;code&gt;float&lt;/code&gt;ed left so that they stack onto each other.&lt;br /&gt;&lt;br /&gt;Notice that there is an empty &lt;code&gt;div&lt;/code&gt; used to create a new line; this is not an ideal situation as it adds to the HTML code bloat (here’s a &lt;a href="http://www.positioniseverything.net/easyclearing.html"&gt;solution&lt;/a&gt; to that, but it seems to be too much of a CSS hack), but it’s a simple solution to the problem of creating new lines (and also another problem where the floating element hangs out of the container, IE does automatic clearing, but that’s in &lt;a href="http://www.positioniseverything.net/easyclearing.html"&gt;violation&lt;/a&gt; of the &lt;a href="http://w3c.org/"&gt;W3C&lt;/a&gt; standards)&lt;br /&gt;&lt;br /&gt;Now, the good thing about these floating fields is that you can rearrange them at will without having to shift the other fields.&lt;br /&gt;&lt;br /&gt;An added benefit of this is method is that if the container shrinks, the fields will automatically move to a new line, reducing horizontal scrolling.&lt;br /&gt;&lt;br /&gt;This is just the basics, there are many variations, have fun.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-114074111819856486?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/114074111819856486/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=114074111819856486' title='78 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114074111819856486'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114074111819856486'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/floating-forms-with-css.html' title='Floating Forms with CSS'/><author><name>adam</name><uri>http://www.blogger.com/profile/11299221547922099901</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17953937139262306140'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>78</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-114045152583539146</id><published>2006-02-20T11:01:00.000-05:00</published><updated>2006-02-20T11:05:25.873-05:00</updated><title type='text'>Rails, Grails, Sails, Nails, Fails...</title><content type='html'>So this entry might not be entirely coherent... it's Monday morning, don't expect it to make much sense.&lt;br /&gt;&lt;br /&gt;This morning I read a message on the &lt;a href="http://webtest.canoo.com/webtest/manual/WebTestHome.html"&gt;Canoo Webtest&lt;/a&gt; mailing list, about &lt;a href="http://grails.codehaus.org/"&gt;Grails&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;The upcoming Grails project will include sophisticated support for Canoo&lt;br /&gt;WebTest.&lt;br /&gt;&lt;br /&gt;Grails is an open-source application framework based on Groovy. see&lt;br /&gt;http://grails.codehaus.org .&lt;br /&gt;&lt;br /&gt;Grails allows easy defintion of domain classes, views, and controllers from&lt;br /&gt;scaffolded artifacts. For these artifacts, it also scaffolds WebTests, see&lt;br /&gt;http://grails.codehaus.org/Functional+Testing .&lt;br /&gt;&lt;br /&gt;Grails is comparable to Ruby on Rails but runs on the Java platform&lt;br /&gt;leveraging the  power of J2EE, Spring, and Hibernate. It even works with&lt;br /&gt;existing Java domain classes and mappings.&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, a small side note here. Canoo is, in my opinion, the best end-to-end functional web testing solution currently available, and has been adopted with great success at my company (&lt;a href="http://wtr.rubyforge.org/"&gt;Watir&lt;/a&gt; and &lt;a href="http://www.openqa.org/selenium/"&gt;Selenium&lt;/a&gt; should probably be mentioned here as well).  Anyway, point is - I respect the Canoo folks, and when they put their support behind something, that is a good reason to take a look at that project.&lt;br /&gt;&lt;br /&gt;Of course, Grails doesn't seem to be the only Ruby-on-Rails-in-Java project (RoRiJ? what a dumb acronym. I promise not to use it again).  There's also &lt;a href="http://blog.opensails.org/"&gt;Sails&lt;/a&gt;, and the two projects seem to be aware of each other.  And, I'm assuming, there are many others, at varying stages of completion.&lt;br /&gt;&lt;br /&gt;What's the point of this rant, completely unrelated on our progress on Duckstein (more on that later :p)?  Well, it's two-fold.  First, I'm usually pretty skeptical of hype, so I never really took the whole &lt;a href="http://www.rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; deal seriously.  I've wanted to check it out to see what all the fuss was about, but wasn't sure whether to expect actually finding anything of substance... but if imitators are popping up left, right and center, there has to be something there.  Second, because I want to follow these projects in the future... and if I don't make a note of them somewhere, I'll completey forget of their existence by lunch, at best :p&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-114045152583539146?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/114045152583539146/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=114045152583539146' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114045152583539146'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114045152583539146'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/rails-grails-sails-nails-fails.html' title='Rails, Grails, Sails, Nails, Fails...'/><author><name>Alex</name><uri>http://www.blogger.com/profile/05017624895316103348</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='12320622651016527192'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-114006467648995240</id><published>2006-02-15T23:26:00.000-05:00</published><updated>2006-02-15T23:38:52.916-05:00</updated><title type='text'>You kicked my browser.</title><content type='html'>You know, Sometimes we wonder why as programmers we have to deal with six browsers, and multiple versions of each browser. Why doesnt someone simply write a super browser of sorts? You have the major players - IE, Firefox, Safari, Konqueror, Opera, and Netscape 4 (heh.. man is netscape 4 in a category of its own): Why doesn't someone write a plugin based browser - like a super browser. The idea: Write a parent that will have plugins for each of these major child browsers. &lt;br /&gt;&lt;br /&gt;Then: Let coders specify (sort of like specifying fonts), which browser their page works best on, and then which is the next best browser for their page and so on.. This super-mother browser will pick up this tag and open that page in the appropriate tab - maybe even open it in the same screen (hide a layer.. show the next?), making it transparent to the end user. &lt;br /&gt;&lt;br /&gt;This is ofcourse a massive hack proposition until everyone starts conforming to standards.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-114006467648995240?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/114006467648995240/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=114006467648995240' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114006467648995240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114006467648995240'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/you-kicked-my-browser.html' title='You kicked my browser.'/><author><name>Vishal</name><uri>http://www.blogger.com/profile/03281033687762661397</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='13267231438732737005'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-114005023492617547</id><published>2006-02-15T19:11:00.000-05:00</published><updated>2006-02-16T16:11:05.170-05:00</updated><title type='text'>User authentication using Tapestry - Part 3</title><content type='html'>This is the third, and probably final, entry on user authentication with Tapestry.  And only one day after the last one... man, I'm on a roll!&lt;br /&gt;&lt;br /&gt;This entry is about tackling the big bad dragon of object-oriented programming, and coming up with something that a) abstracts some of the plumbing work of user authentication so we don't have to do it on every page in the application, and b) can be used for different Tapestry projects without any changes.&lt;br /&gt;&lt;br /&gt;I've added two new classes that should be application-independent (though not Tapestry-independent).  One is a wrapper user object that we can store in the session as an ASO; the other is an abstract Tapestry page class that holds what we need to deal with the ASO.&lt;br /&gt;&lt;br /&gt;First, the user class:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; TapestryUser {&lt;br /&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; boolean loggedIn;&lt;br /&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; Object user;&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; TapestryUser() {&lt;br /&gt;        user = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;        loggedIn = &lt;span class="kwrd"&gt;false&lt;/span&gt;;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; boolean isLoggedIn() {&lt;br /&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; loggedIn;&lt;br /&gt;    }&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; Object getUser() {&lt;br /&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; user;&lt;br /&gt;    }&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; setLoggedIn(boolean loggedIn) {&lt;br /&gt;        &lt;span class="kwrd"&gt;this&lt;/span&gt;.loggedIn = loggedIn;&lt;br /&gt;    }&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; setUser(Object user) {&lt;br /&gt;        &lt;span class="kwrd"&gt;this&lt;/span&gt;.user = user;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Pretty simple.  According to &lt;a href="http://www.agileskills2.org/EWDT/"&gt;Enjoy Web Development with Tapestry&lt;/a&gt;, ASOs must have a constructor with no arguments (check), and implement Serializable.  I actually forgot about the Serializable part until I started writing this... but it seems to work.  So, who knows - if I run into any problems later, this'll be one thing to check, I guess.&lt;br /&gt;&lt;br /&gt;Next, the page class:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;abstract&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; AuthenticatedPage extends BasePage {&lt;br /&gt;    &lt;br /&gt;    @InjectStateFlag(&lt;span class="str"&gt;"tapestryUser"&lt;/span&gt;)&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;abstract&lt;/span&gt; boolean getTapestryUserExists();&lt;br /&gt;&lt;br /&gt;    @InjectState(&lt;span class="str"&gt;"tapestryUser"&lt;/span&gt;)&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;abstract&lt;/span&gt; TapestryUser getTapestryUser();    &lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;abstract&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; setTapestryUser(TapestryUser tapestryUser);&lt;br /&gt;    &lt;br /&gt;    &lt;span class="rem"&gt;/**&lt;/span&gt;&lt;br /&gt;&lt;span class="rem"&gt;     * Return true if the current session belongs to a logged&lt;/span&gt;&lt;br /&gt;&lt;span class="rem"&gt;     * in user.&lt;/span&gt;&lt;br /&gt;&lt;span class="rem"&gt;     * @author Alex&lt;/span&gt;&lt;br /&gt;&lt;span class="rem"&gt;     */&lt;/span&gt;    &lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; boolean getLoggedIn() {&lt;br /&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (getTapestryUserExists() &amp;amp;&amp;amp; getTapestryUser().isLoggedIn())&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;true&lt;/span&gt;;&lt;br /&gt;        }&lt;br /&gt;        &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;false&lt;/span&gt;;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    &lt;span class="rem"&gt;/**&lt;/span&gt;&lt;br /&gt;&lt;span class="rem"&gt;     * Store user in the httpsession as logged in.  &lt;/span&gt;&lt;br /&gt;&lt;span class="rem"&gt;     * @author Alex&lt;/span&gt;&lt;br /&gt;&lt;span class="rem"&gt;     */&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; setLoggedIn(Object user) {&lt;br /&gt;        getTapestryUser().setUser(user);&lt;br /&gt;        getTapestryUser().setLoggedIn(&lt;span class="kwrd"&gt;true&lt;/span&gt;);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Again, pretty simple.  This simply takes all the stuff we put in our Home class last time, and packages it nicely for easy reusability.  The last step is to declare the ASO for Hivemind:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&amp;lt;?xml version=&lt;span class="str"&gt;"1.0"&lt;/span&gt;?&amp;gt;&lt;br /&gt;&amp;lt;module id=&lt;span class="str"&gt;"com.kharthick.beer"&lt;/span&gt; version=&lt;span class="str"&gt;"1.0.0"&lt;/span&gt;&amp;gt;&lt;br /&gt;    &amp;lt;contribution configuration-id=&lt;span class="str"&gt;"tapestry.state.ApplicationObjects"&lt;/span&gt;&amp;gt;&lt;br /&gt;        &amp;lt;state-&lt;span class="kwrd"&gt;object&lt;/span&gt; name=&lt;span class="str"&gt;"tapestryUser"&lt;/span&gt; scope=&lt;span class="str"&gt;"session"&lt;/span&gt;&amp;gt;&lt;br /&gt;            &amp;lt;create-instance &lt;span class="kwrd"&gt;class&lt;/span&gt;=&lt;span class="str"&gt;"com.kharthick.authentication.TapestryUser"&lt;/span&gt;/&amp;gt;&lt;br /&gt;        &amp;lt;/state-&lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt;&lt;br /&gt;    &amp;lt;/contribution&amp;gt;&lt;br /&gt;&amp;lt;/module&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Using these two classes is even easier.  For any page in our Tapestry application that has a login form, the page definition will start with something like:&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;abstract&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; Home extends AuthenticatedPage &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;(remember that previously the Home class extended BasePage).  Now you can accept the user login/password and verify them any way you want to, and simply call setLoggedIn once you're happy:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;        MutableUser mutableUser = DummyFactory.getMutableUser(username, password);&lt;br /&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (mutableUser != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="rem"&gt;// remember that this user has logged in.&lt;/span&gt;&lt;br /&gt;            &lt;b&gt;setLoggedIn(mutableUser);&lt;/b&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; getHome();&lt;br /&gt;        }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Pages that only need to check whether the user is logged on, and not actually log on, are even easier.  There is NO Java code needed beyond "extends AuthenticatedPage" instead of "extends BasePage", and the page specification can simply do something like:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="@If"&lt;/span&gt; &lt;span class="attr"&gt;condition&lt;/span&gt;&lt;span class="kwrd"&gt;="ognl:loggedIn"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Accessing any user object properties or methods is as simple as value="ognl:tapestryUser.user.username" as long as the page class extends AuthenticatedPage.&lt;br /&gt;&lt;br /&gt;This mechanism doesn't provide any information beyond whether the user is logged in or not, but it does provide at least two ways to extend it for more complicated authentication (such as permissions).  One way would be to extend AuthenticatedPage with application-specific methods; the other would be to keep such methods in the application-specific user object.&lt;br /&gt;&lt;br /&gt;Well, there you have it! By using a clever combination of the Structural-Beam-Under-Tunnel and Monk-Under-Tree-Feeding-Small-Bird design patterns, we have achieved OOP nirvana.  One down, only four more ${vowel}-oriented-programming paradigms to go!&lt;br /&gt;&lt;br /&gt;This post is part of a series:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://sanium.net/blog/2006/02/user-authentication-using-tapestry.html"&gt;User authentication using Tapestry - Part 1&lt;/a&gt;&lt;br /&gt;&lt;a href="http://sanium.net/blog/2006/02/user-authentication-using-tapestry_14.html"&gt;User authentication using Tapestry - Part 2&lt;/a&gt;&lt;br /&gt;User authentication using Tapestry - Part 3&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-114005023492617547?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/114005023492617547/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=114005023492617547' title='71 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114005023492617547'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114005023492617547'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/user-authentication-using-tapestry_15.html' title='User authentication using Tapestry - Part 3'/><author><name>Alex</name><uri>http://www.blogger.com/profile/05017624895316103348</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='12320622651016527192'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>71</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-114004674088182552</id><published>2006-02-15T18:21:00.000-05:00</published><updated>2006-02-15T18:39:00.893-05:00</updated><title type='text'>Belated Introduction</title><content type='html'>Hi, for all those who don't know me, and I would say that's a lot, I'm a high school senior (soon-to-be college freshmen) that is "deeply" interested in computer science and engineering. I am, however, not as knowledgeable as the other chaps(lol) that contribute. Learning right now is my main concern. Well, let's see if I'll ever contribute anything meaningful.. lol&lt;br /&gt;&lt;br /&gt;I do have a project I'm working on, a game called &lt;a href="http://krakensden.dyndns.org/primitivewar/"&gt;Primitive Wars&lt;/a&gt;, that I am overhauling right now. It will, in my hopeful vision, be a bona fide turn based strategy that has elements similar to the Advance Wars series. Any artists, musicians, story writers, programmers, or game designers are welcome to help me. =]&lt;br /&gt;&lt;br /&gt;Ciao for now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-114004674088182552?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/114004674088182552/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=114004674088182552' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114004674088182552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114004674088182552'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/belated-introduction.html' title='Belated Introduction'/><author><name>aaron</name><email>noreply@blogger.com</email></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-114004147158128868</id><published>2006-02-15T17:05:00.000-05:00</published><updated>2006-02-15T17:11:11.590-05:00</updated><title type='text'>Fun With Javascript</title><content type='html'>I recently had some fun with animating a div with javascript.&lt;br /&gt;&lt;br /&gt;I was just playing with showing/hiding a &lt;code&gt;div&lt;/code&gt; tab and decided to try animating it growing...&lt;br /&gt;&lt;br /&gt;I created a &lt;code&gt;anim&lt;/code&gt; function for animating, but alas, it didn't work. well, I couldn't figure out how to pass objects using the javascript &lt;code&gt;setTimeout()&lt;/code&gt; method.&lt;br /&gt;&lt;br /&gt;after some searching, I found this interesting site and began writing this code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    function getAnimDiv(obj, change, current, end) {&lt;br /&gt;      return (function() {&lt;br /&gt;      &lt;br /&gt;        current += change;&lt;br /&gt;        if(current &gt;= end) {&lt;br /&gt;          obj.style.height = end+"px";&lt;br /&gt;        } else {&lt;br /&gt;          obj.style.height = current+"px";&lt;br /&gt;          anim(obj,change,current,end);&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;      });&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    function anim(obj, change, current, end) {&lt;br /&gt;      var animRef = getAnimDiv(obj, change, current, end);&lt;br /&gt;      setTimeout(animRef, 10);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I'll leave it's inner workings to the reader, but I will tell you that &lt;code&gt;obj&lt;/code&gt; is the &lt;code&gt;div&lt;/code&gt; object.&lt;br /&gt;&lt;br /&gt;Is there a better way to do this? Maybe, if you figure it out, let me know.&lt;br /&gt;&lt;br /&gt;Oh yeah, this works in both IE and Firefox.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-114004147158128868?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/114004147158128868/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=114004147158128868' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114004147158128868'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/114004147158128868'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/fun-with-javascript.html' title='Fun With Javascript'/><author><name>adam</name><uri>http://www.blogger.com/profile/11299221547922099901</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17953937139262306140'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-113997740955531176</id><published>2006-02-14T22:37:00.000-05:00</published><updated>2006-02-16T16:13:03.176-05:00</updated><title type='text'>User authentication using Tapestry - Part 2</title><content type='html'>For my exciting sequel to the first part of &lt;a href="http://sanium.net/blog/2006/02/user-authentication-using-tapestry.html"&gt;User authentication using Tapestry&lt;/a&gt;, I will try to cover how to actually do something based on whether the user is logged in or not.  Amazing, isn't it? (just wait till part three - we'll get to defeat the evil forces of Sauron! ...or something equally adventurous).&lt;br /&gt;&lt;br /&gt;First, recall that last time we stored the user object in an application state object, which is accessed by Tapestry using something like this:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;    @InjectState(&lt;span class="str"&gt;"mutableUser"&lt;/span&gt;)&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;abstract&lt;/span&gt; MutableUser getMutableUser();    &lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;abstract&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; setMutableUser(MutableUser mutableUser);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;When the user successfully logs in, we set this to something that makes sense, in the hope that we'll check it later to verify that the user is logged in.  The way we set it was with:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;            getMutableUser().copyFrom(mutableUser);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, notice that this is the very first time we're calling getMutableUser() to access the ASO (don't confuse it with the call to DummyFactory).  So where is it coming from, since we never created it?  Well, it turns out that Tapestry (or Hivemind more likely, I'm never too clear where all this stuff is handled) initializes the object the very first time it's accessed.  This would presumably be why it needs an empty constructor.&lt;br /&gt;&lt;br /&gt;So how do we check if the user is logged in, if trying to access the MutableUser object actually creates it?  Pretty simple - with a little beauty named a &lt;a href="http://jakarta.apache.org/tapestry/tapestry-annotations/index.html#InjectStateFlag"&gt;State Flag&lt;/a&gt;, which is defined like this and does pretty much what you'd expect it to:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;    @InjectStateFlag(&lt;span class="str"&gt;"mutableUser"&lt;/span&gt;)&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;abstract&lt;/span&gt; boolean getMutableUserExists();&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;OK, enough chit-chat - let's get to actually doing something with this information.  I made the login box on the home page show up if the user is not logged in - as soon as the user logs in, I made the page disappear and show a message welcoming the user, and a logout link.&lt;br /&gt;&lt;br /&gt;Here's the HTML (true to form, I completely failed to separate all the parameter bindings into the page definition file... maybe for the final code :p)&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="@If"&lt;/span&gt; &lt;span class="attr"&gt;condition&lt;/span&gt;&lt;span class="kwrd"&gt;="ognl:loggedIn"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;      Welcome, &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="@Insert"&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;="ognl:mutableUser.username"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;!&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;style&lt;/span&gt;&lt;span class="kwrd"&gt;="font-weight: bold;"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;User Actions: &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;       My Profile&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;       Manage Users&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;a&lt;/span&gt; &lt;span class="attr"&gt;href&lt;/span&gt;&lt;span class="kwrd"&gt;="#"&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="@ExternalLink"&lt;/span&gt; &lt;span class="attr"&gt;page&lt;/span&gt;&lt;span class="kwrd"&gt;="EditMeeting"&lt;/span&gt; &lt;span class="attr"&gt;parameters&lt;/span&gt;&lt;span class="kwrd"&gt;="ognl:-1"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Create Meeting&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;a&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="@ServiceLink"&lt;/span&gt; &lt;span class="attr"&gt;service&lt;/span&gt;&lt;span class="kwrd"&gt;="literal:restart"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Logout&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="@Else"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="loginForm"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;      &lt;br /&gt;          Username:&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="text"&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="username"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;        Password:&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="password"&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="password"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="Login"&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;="Login"&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="submit"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;              &lt;br /&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;style&lt;/span&gt;&lt;span class="kwrd"&gt;="color: red"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="errorMsg"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;      &lt;br /&gt;        Don't have an account?&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;a&lt;/span&gt; &lt;span class="attr"&gt;href&lt;/span&gt;&lt;span class="kwrd"&gt;="register.html"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Register!&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;a&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;br&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Don't think there's anything fancy - the only thing worth noting is the condition of the @If component: condition="ognl:loggedIn".  Here's the corresponding Java code for this condition:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; boolean getLoggedIn() {&lt;br /&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (getMutableUserExists())&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;true&lt;/span&gt;;&lt;br /&gt;        }&lt;br /&gt;        &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;false&lt;/span&gt;;&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, there are two things worth noting here.  First, the condition is ognl:loggedIn, not getLoggedIn.  I'm cheating here a bit, because I'm counting on Tapestry's behaviour to call a getter for a boolean named "loggedIn".  The way I had this coded at first was something like &lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="@If"&lt;/span&gt; &lt;span class="attr"&gt;condition&lt;/span&gt;&lt;span class="kwrd"&gt;="ognl:loggedIn"&lt;/span&gt; &lt;span class="attr"&gt;listener&lt;/span&gt;&lt;span class="kwrd"&gt;="isLoggedIn"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;where isLoggedIn actually set a private boolean field named loggedIn.  This led to the strange behaviour that the login box would only disappear on the SECOND successful login (or once the resulting page after a login was reloaded) - which most likely indicates that a) the changes made by the listener take effect too late to affect the rendering of the if component, and b) I still don't really understand the whole page rendering cycle and form submission behind Tapestry.&lt;br /&gt;&lt;br /&gt;The second thing to notice is that we're simply assuming the user is logged in based on getMutableUserExists().  This isn't a very safe assumption, since the ASO is created whenever it's accessed - a more valid check would be something like getMutableUserExists() followed by getMutableUser.isLoggedIn().  We don't really have a proper tapestry user object, so this isn't in yet.&lt;br /&gt;&lt;br /&gt;Finally, how to log out?  The simplest way would be (assuming we had a proper user object) something like getMutableUser.setLoggedIn(false).  The somewhat more complicated way would be to remove the mutableUser object from the session - I still need to figure out how to do this.  (TODO - update here if I figure it out!)&lt;br /&gt;&lt;br /&gt;Of course, I chose neither of the above, in favour of the most PERMANENT way - we just destory the entire session!  Brilliant, isn't it?  All it takes is a &lt;a href="http://jakarta.apache.org/tapestry/tapestry/ComponentReference/ServiceLink.html"&gt;ServiceLink&lt;/a&gt; to the restart &lt;a href="http://jakarta.apache.org/tapestry/tapestry/apidocs/org/apache/tapestry/engine/IEngineService.html"&gt;service&lt;/a&gt; (did I mention how much I hate trying to find something in the Tapestry documentation?).  The HTML, as seen above:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="@ServiceLink"&lt;/span&gt; &lt;span class="attr"&gt;service&lt;/span&gt;&lt;span class="kwrd"&gt;="literal:restart"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Logout&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And that's that for today!&lt;br /&gt;&lt;br /&gt;Coming up next: how to set up a more general user authentication system, or possibly some &lt;a href="http://tacos.sourceforge.net/"&gt;Tacos AJAX goodness&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;This post is part of a series:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://sanium.net/blog/2006/02/user-authentication-using-tapestry.html"&gt;User authentication using Tapestry - Part 1&lt;/a&gt;&lt;br /&gt;User authentication using Tapestry - Part 2&lt;br /&gt;&lt;a href="http://sanium.net/blog/2006/02/user-authentication-using-tapestry_15.html"&gt;User authentication using Tapestry - Part 3&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-113997740955531176?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/113997740955531176/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=113997740955531176' title='76 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113997740955531176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113997740955531176'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/user-authentication-using-tapestry_14.html' title='User authentication using Tapestry - Part 2'/><author><name>Alex</name><uri>http://www.blogger.com/profile/05017624895316103348</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='12320622651016527192'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>76</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-113994106264305587</id><published>2006-02-14T13:06:00.000-05:00</published><updated>2006-02-14T13:17:42.653-05:00</updated><title type='text'>Perils of Enterprise Debugging</title><content type='html'>Debugging by itself is not an entirely un-enjoyable experience.&lt;br /&gt;Debugging someone else’s code is less so.&lt;br /&gt;Debugging someone else’s code in an enterprise environment where said person is long gone is a perilous activity at best.&lt;br /&gt;&lt;br /&gt;There’s a defect at work. Only the first part of the page is loading up. The page is the last page on a sequence of pages.&lt;br /&gt;&lt;br /&gt;Step 1. reproduce the defect on the remote server, ~ 5 minutes&lt;br /&gt;Step 2. look at logs on the remote server, ~ 5 minutes&lt;br /&gt;Step 3. D’oh, can’t find any errors, load up latest version from source control locally, ~ 10 minutes&lt;br /&gt;Step 4. Insert debug statements where I suspect the error occurs, ~ 1 minute&lt;br /&gt;Step 5. Start up local Weblogic server, compile and deploy, ~ 10 minutes&lt;br /&gt;Step 6. Test locally, ~ 5 minutes&lt;br /&gt;Step 7. Insert more debug statements, compile, deploy, test, rinse and repeat ~ 20 minutes&lt;br /&gt;&lt;br /&gt;So, after about an hour and a half, I find the cause of the error.&lt;br /&gt;&lt;br /&gt;A type in the name of resource.&lt;br /&gt;&lt;br /&gt;Considering everything, this bug isn’t too bad.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-113994106264305587?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/113994106264305587/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=113994106264305587' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113994106264305587'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113994106264305587'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/perils-of-enterprise-debugging.html' title='Perils of Enterprise Debugging'/><author><name>adam</name><uri>http://www.blogger.com/profile/11299221547922099901</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17953937139262306140'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-113985821062873332</id><published>2006-02-13T14:09:00.000-05:00</published><updated>2006-02-13T14:16:50.646-05:00</updated><title type='text'>World's most useless class?</title><content type='html'>&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Courier New , Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; Counter {&lt;br /&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; i;&lt;br /&gt;    &lt;span class="rem"&gt;/**&lt;/span&gt;&lt;br /&gt;&lt;span class="rem"&gt;     * @return Returns the i.&lt;/span&gt;&lt;br /&gt;&lt;span class="rem"&gt;     */&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; increment() {&lt;br /&gt;        &lt;span class="kwrd"&gt;this&lt;/span&gt;.i++;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; String getString() {&lt;br /&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; Integer.toString(getI());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; getI() {&lt;br /&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; i;&lt;br /&gt;    }&lt;br /&gt;    &lt;span class="rem"&gt;/**&lt;/span&gt;&lt;br /&gt;&lt;span class="rem"&gt;     * @param i The i to set.&lt;/span&gt;&lt;br /&gt;&lt;span class="rem"&gt;     */&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; setI(&lt;span class="kwrd"&gt;int&lt;/span&gt; i) {&lt;br /&gt;        &lt;span class="kwrd"&gt;this&lt;/span&gt;.i = i;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;What is the point of this??&lt;br /&gt;&lt;br /&gt;To avoid incrementing by something other than 1 by mistake?  To make it more obvious that the variable is a counter or that you're incrementing (as opposed to the cryptic i++)?  To pass the counter variable around?  To be able to extend your counter's functionality in exciting new ways without affecting the code that uses it?&lt;br /&gt;&lt;br /&gt;What's wrong with good old int i??&lt;br /&gt;&lt;br /&gt;Discuss.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-113985821062873332?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/113985821062873332/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=113985821062873332' title='79 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113985821062873332'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113985821062873332'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/worlds-most-useless-class.html' title='World&apos;s most useless class?'/><author><name>Alex</name><uri>http://www.blogger.com/profile/05017624895316103348</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='12320622651016527192'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>79</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-113960001121766412</id><published>2006-02-10T14:05:00.000-05:00</published><updated>2006-02-10T14:33:31.296-05:00</updated><title type='text'>I don't get regular expressions.</title><content type='html'>I mean, sure, I get the general idea of regular expressions, and on a good day I can even get something done with them.&lt;br /&gt;&lt;br /&gt;Take the &lt;a href="http://jakarta.apache.org/oro/"&gt;Jakarta Oro regular expression engine&lt;/a&gt;, for example - based on the Perl regular expression rules, and used (among many others, I'm sure) by &lt;a href="http://jakarta.apache.org/jmeter/"&gt;Jmeter&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I spent far too long trying to find a regex to match the following:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;B&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="calendarTextBlack"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;12:00 PM - 1:00 PM&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;B&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;  &lt;br /&gt;        &lt;br /&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;B&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="calendarTextBlack"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Available&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;B&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="calendarTextBlack"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;  TOR_099_NN_S1 &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note the line breaks in there - always fun for regexes.&lt;br /&gt;&lt;br /&gt;I tried several variations on the theme of:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;12:00 PM - 1:00 PM.*[.\r\n\s]*Available.*TOR_099_NN_S1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;None of them worked, not in Jmeter, and not in the &lt;a href="http://jakarta.apache.org/oro/demo.html"&gt;Oro Demonstration Applet&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Eventually, I tried&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;12:00 PM - 1:00 PM.*[\r\n\s]*.*Available.*TOR_099_NN_S1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Lo and behold, it worked.&lt;br /&gt;&lt;br /&gt;What in the world is the difference between [.\r\n\s]* and [\r\n\s]*.* escapes me.  If anything, you'd think the second would return a subset of the results of the first.  Guess not...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-113960001121766412?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/113960001121766412/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=113960001121766412' title='96 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113960001121766412'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113960001121766412'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/i-dont-get-regular-expressions.html' title='I don&apos;t get regular expressions.'/><author><name>Alex</name><uri>http://www.blogger.com/profile/05017624895316103348</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='12320622651016527192'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>96</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-113959229141150240</id><published>2006-02-10T11:14:00.000-05:00</published><updated>2006-02-10T12:50:17.503-05:00</updated><title type='text'>Web UIs</title><content type='html'>The problem with designing web interfaces is the lack of support for standards.&lt;br /&gt;With netscape 4 out of the way, the biggest culprit would be internet explorer.&lt;br /&gt;For example, while tweaking this blog to accommodate alex's ridiculously long post, I ran into several of ie's quirks.&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;the css property &lt;code&gt;overflow:auto;&lt;/code&gt; (with a width constraint) for ie introduces not only the horizontal scroll bar (correct) but also a vertical scroll bar (bad) because the horizontal scrollbar took up extra vertical space in the element.&lt;br /&gt;firefox manages to handle this correctly, making enough vertical space and displaying only the horizontal.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;internet explorer doesn't support the css max-height property.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;The fixes / compromises:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;I used the &lt;code&gt;overflow-x&lt;/code&gt; and &lt;code&gt;overflow-y&lt;/code&gt; properties, I was able to force ie to only display the horizontal scrollbar, along with a &lt;code&gt;padding-bottom:1em;&lt;/code&gt; to take care of the last line being obscured by the scrollbar.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I had to compromise on this one, making the height of each post a fixed number.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;The ideal solution for this problem is having all browsers support the standards, but since that's not going to happen, the next best thing would be to design for graceful degradation.&lt;br /&gt;&lt;br /&gt;here's an interesting article on designing for &lt;a href="http://www.svendtofte.com/code/max_width_in_ie/"&gt;usability&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-113959229141150240?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/113959229141150240/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=113959229141150240' title='79 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113959229141150240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113959229141150240'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/web-uis.html' title='Web UIs'/><author><name>adam</name><uri>http://www.blogger.com/profile/11299221547922099901</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17953937139262306140'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>79</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-113954528552280710</id><published>2006-02-09T22:36:00.000-05:00</published><updated>2006-02-16T16:11:45.556-05:00</updated><title type='text'>User authentication using Tapestry</title><content type='html'>Ahh, the excitement of learning Tapestry.  I live so dangerously!&lt;br /&gt;&lt;br /&gt;Today I tackled the problem of user authentication with Tapestry (aka login).  Armed with Chapter 4 of &lt;a href="http://www.agileskills2.org/EWDT/"&gt;Enjoy Web Development with Tapestry&lt;/a&gt;, it ended up being fairly simple.  I did end up wasting almost an hour because of something that wasn't clear, and that's probably why I'm less enthusiastic than I might be otherwise - but all in all it's not too bad.&lt;br /&gt;&lt;br /&gt;In a nutshell, Tapestry uses Hivemind to store objects in a user's session.  When a user logs on successfully, the corresponding User object is stored in that session - pages that need authentication then check whether that object exists.   Pretty simple.&lt;br /&gt;&lt;br /&gt;So how is it done?  The book I'm using explains it fairly well (and chapters 1-4 are available online), so here are just the essential parts of the code.&lt;br /&gt;&lt;br /&gt;First, the login form.  Nothing fancy, just two fields and a submit button.  It's a separate html file for now, for simplicity, but this'd most likely be inside the home page.&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Login&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;title&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;head&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;h1&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Login&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;h1&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;style&lt;/span&gt;&lt;span class="kwrd"&gt;="color: red"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="errorMsg"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;span&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="loginForm"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;table&lt;/span&gt; &lt;span class="attr"&gt;border&lt;/span&gt;&lt;span class="kwrd"&gt;="0"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;tr&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Username:&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="text"&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="username"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;tr&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;tr&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;Password:&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="password"&lt;/span&gt; &lt;span class="attr"&gt;jwcid&lt;/span&gt;&lt;span class="kwrd"&gt;="password"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;tr&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;tr&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;input&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="submit"&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;="Login"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;td&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;tr&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;table&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;form&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;body&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;html&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;Next, the corresponding page specification file:&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="html"&gt;xml&lt;/span&gt; &lt;span class="attr"&gt;version&lt;/span&gt;&lt;span class="kwrd"&gt;="1.0"&lt;/span&gt; &lt;span class="attr"&gt;encoding&lt;/span&gt;&lt;span class="kwrd"&gt;="UTF-8"&lt;/span&gt;?&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;!&lt;/span&gt;&lt;span class="html"&gt;DOCTYPE&lt;/span&gt; &lt;span class="attr"&gt;page-specification&lt;/span&gt; &lt;span class="attr"&gt;PUBLIC&lt;/span&gt;&lt;br /&gt;  &lt;span class="kwrd"&gt;"-//Apache Software Foundation//Tapestry Specification 4.0//EN"&lt;/span&gt; &lt;br /&gt;  &lt;span class="kwrd"&gt;"http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;page-specification&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;component&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="loginForm"&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="Form"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;binding&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="listener"&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;="listener:onLogin"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;component&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;component&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="username"&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="TextField"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;binding&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="value"&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;="username"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;component&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;component&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="password"&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="TextField"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;binding&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="value"&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;="password"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;binding&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="hidden"&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;="true"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;component&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;component&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="errorMsg"&lt;/span&gt; &lt;span class="attr"&gt;type&lt;/span&gt;&lt;span class="kwrd"&gt;="Delegator"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;binding&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="delegate"&lt;/span&gt; &lt;span class="attr"&gt;value&lt;/span&gt;&lt;span class="kwrd"&gt;="beans.delegate.firstError"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;component&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;page-specification&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;Again, pretty simple, the only part we really care about is the listener:onLogin which is the listener that's called by the form submit.&lt;br /&gt;&lt;br /&gt;Now, the fun part - the corresponding Java code for the login page (or the interesting parts, anyway):&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;    @InjectState(&lt;span class="str"&gt;"mutableUser"&lt;/span&gt;)&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;abstract&lt;/span&gt; MutableUser getMutableUser();&lt;br /&gt;    &lt;br /&gt;    @Bean&lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;abstract&lt;/span&gt; ValidationDelegate getDelegate();&lt;br /&gt;    &lt;br /&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; String onLogin() {&lt;br /&gt;        MutableUser mutableUser = DummyFactory.getMutableUser(username, password);&lt;br /&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (mutableUser != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;br /&gt;        {&lt;br /&gt;            &lt;span class="rem"&gt;// remember that this user has logged in.&lt;/span&gt;&lt;br /&gt;            getMutableUser().copyFrom(mutableUser);&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="str"&gt;"Login"&lt;/span&gt;;&lt;br /&gt;        } &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;br /&gt;            &lt;span class="rem"&gt;// display an error and show the Login page again.&lt;/span&gt;&lt;br /&gt;            ValidationDelegate &lt;span class="kwrd"&gt;delegate&lt;/span&gt; = getDelegate();&lt;br /&gt;            &lt;span class="kwrd"&gt;delegate&lt;/span&gt;.setFormComponent(&lt;span class="kwrd"&gt;null&lt;/span&gt;);&lt;br /&gt;            &lt;span class="kwrd"&gt;delegate&lt;/span&gt;.record(&lt;span class="str"&gt;"Login failed"&lt;/span&gt;, &lt;span class="kwrd"&gt;null&lt;/span&gt;);&lt;br /&gt;            &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;br /&gt;        }&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;For the most part, this is simple.  When onLogin() is called, Tapestry tries to obtain the user object for the given username/password - if successful then reload the page (the return "Login" - this should probably be a page forward), or output an error.&lt;br /&gt;&lt;br /&gt;The cool part here is the     @InjectState("mutableUser")  line.  This accesses an &lt;a href="http://jakarta.apache.org/tapestry/UsersGuide/state.html#state.aso"&gt;Application State Object&lt;/a&gt; named mutableUser.  ASO's are objects that persist during a user's session, are stored in the HttpSession, and as far as I can tell are managed by HiveMind.  On a successful logon, the getMutableUser().copyFrom(mutableUser); line copies the contents of the user object we just got into the mutableUser ASO.&lt;br /&gt;&lt;br /&gt;Side note on ASO's: since they're passed in the HttpSession, they need to implement Serializable (hence the MutableUser class as opposed to the immutable User object we're using in Duckstein so far).  They also need an empty constructor, and obviously some way to get the data into them, such as copyFrom above.&lt;br /&gt;&lt;br /&gt;So how do we tell Tapestry (or rather, Hivemind) about our mutableUser ASO?  That'd be the hivemodule.xml file:&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="html"&gt;xml&lt;/span&gt; &lt;span class="attr"&gt;version&lt;/span&gt;&lt;span class="kwrd"&gt;="1.0"&lt;/span&gt;?&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;module&lt;/span&gt; &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;="com.kharthick.beer"&lt;/span&gt; &lt;span class="attr"&gt;version&lt;/span&gt;&lt;span class="kwrd"&gt;="1.0.0"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;contribution&lt;/span&gt; &lt;span class="attr"&gt;configuration-id&lt;/span&gt;&lt;span class="kwrd"&gt;="tapestry.state.ApplicationObjects"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;state-object&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="mutableUser"&lt;/span&gt; &lt;span class="attr"&gt;scope&lt;/span&gt;&lt;span class="kwrd"&gt;="session"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;create-instance&lt;/span&gt; &lt;span class="attr"&gt;class&lt;/span&gt;&lt;span class="kwrd"&gt;="com.kharthick.beer.info.MutableUser"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;state-object&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;contribution&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;module&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;(save yourself some time and just put this file under your context/WEB-INF folder, by the way - I wasted a lot of time because I put it in the wrong place, which is hard to do actually, since Hivemind looks for it almost everywhere).&lt;br /&gt;&lt;br /&gt;In summary:&lt;br /&gt;&lt;br /&gt;I now have a login page (http://127.0.0.1:8080/duckstein/app?page=Login&amp;service=page in case you grab the &lt;a href="http://www.sourceforge.net/projects/duckstein"&gt;latest CVS&lt;/a&gt;) that takes any login and password combination, as long as the password is "food".  It then reloads itself.  Not very exciting.  The URL changes though, to include the HttpSession.&lt;br /&gt;&lt;br /&gt;What's next:  doing something based on whether the user is logged in or not.  The book talks about accepting or rejecting an action such as a form submit - I guess we need to figure out more subtle stuff, like showing or not showing parts of a page.&lt;br /&gt;&lt;br /&gt;It might also be useful to write a &lt;a href="http://www.mail-archive.com/tapestry-user@jakarta.apache.org/msg11419.html"&gt;generic page derived from PageRenderListener&lt;/a&gt; that implements the logged in check, and inherit that for pages that need authentication, rather than duplicating the code everywhere.  Might be more challenging, again, since we'll most likely want to show/hide only parts of a page, not the entire page.&lt;br /&gt;&lt;br /&gt;This post is part of a series:&lt;br /&gt;&lt;br /&gt;User authentication using Tapestry - Part 1&lt;br /&gt;&lt;a href="http://sanium.net/blog/2006/02/user-authentication-using-tapestry_14.html"&gt;User authentication using Tapestry - Part 2&lt;/a&gt;&lt;br /&gt;&lt;a href="http://sanium.net/blog/2006/02/user-authentication-using-tapestry_15.html"&gt;User authentication using Tapestry - Part 3&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-113954528552280710?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/113954528552280710/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=113954528552280710' title='151 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113954528552280710'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113954528552280710'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/user-authentication-using-tapestry.html' title='User authentication using Tapestry'/><author><name>Alex</name><uri>http://www.blogger.com/profile/05017624895316103348</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='12320622651016527192'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>151</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-113952094221291897</id><published>2006-02-09T16:35:00.000-05:00</published><updated>2006-02-09T23:26:02.783-05:00</updated><title type='text'>project duckstein</title><content type='html'>let sourceforge tell you about project duckstein.&lt;br /&gt;let me tell you about why we're doing project duckstein.&lt;br /&gt;&lt;br /&gt;the primary stated reason is to learn all sorts of crazy java technologies like ant, hibernate, ajax, tapestry, spring, junit, and more importantly, gain experience in developing application, or as one of my profs would put it: "knowledge embedded in experience". although this is a major reason for the project, it's not the only reason.&lt;br /&gt;&lt;br /&gt;the other driving factor is that i'm lazy. now, this may seem contradictory, but it's not. you see, some of my friends and I have started having meetings where we would taste and review … stuff, so i figured this would be a good chance to kill two (or more) birds with one stone.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-113952094221291897?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/113952094221291897/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=113952094221291897' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113952094221291897'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113952094221291897'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/project-duckstein.html' title='project duckstein'/><author><name>adam</name><uri>http://www.blogger.com/profile/11299221547922099901</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17953937139262306140'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-22146533.post-113945265873256757</id><published>2006-02-08T21:27:00.000-05:00</published><updated>2006-02-08T21:37:38.740-05:00</updated><title type='text'>My introduction</title><content type='html'>Well, I guess I'll follow Adam's lead.&lt;br /&gt;&lt;br /&gt;I'm also an engineer, graduated in '05, living in Toronto, working for a telehealth company, etc. etc.&lt;br /&gt;&lt;br /&gt;I guess my involvement in this whole mess started with a recent discovery: university has taught me very little about programming.  Sure, there was fun stuff like compilers, networking, and OS... and less fun stuff like computational complexity and... uhh... computability (who comes up with those names?).  But we didn't learn any of the USEFUL stuff, like how to build a complex piece of software, what a framework does, design patterns, or best practices...&lt;br /&gt;&lt;br /&gt;Hence, this site.  I'm currently trying to make my way through the Design Patterns book, and through Effective Java.  Trying to apply as much of what I learn as possible on our current project, code-named &lt;a href="http://sourceforge.net/projects/duckstein/"&gt;Duckstein&lt;/a&gt;, and hoping to revive the &lt;a href="http://sourceforge.net/projects/skulepenguins/"&gt;SkulePenguins&lt;/a&gt; project sometime in the near future, possibly using &lt;a href="http://www.ogre3d.org"&gt;Ogre 3D&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Should be interesting to play with all the latest toys in the software development world... especially since I hate anything that's overhyped or more complicated than it has to be. Stay tuned :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/22146533-113945265873256757?l=sanium.net%2Fblog%2Findex.htm'/&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/113945265873256757/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://www.blogger.com/comment.g?blogID=22146533&amp;postID=113945265873256757' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113945265873256757'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/22146533/posts/default/113945265873256757'/><link rel='alternate' type='text/html' href='http://sanium.net/blog/2006/02/my-introduction.html' title='My introduction'/><author><name>Alex</name><uri>http://www.blogger.com/profile/05017624895316103348</uri><email>noreply@blogger.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='12320622651016527192'/></author><thr:total xmlns:thr='http://purl.org/syndication/thread/1.0'>0</thr:total></entry></feed>