<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8840085855436806841</id><updated>2012-01-23T07:31:06.950-05:00</updated><title type='text'>entrepreneurial musings</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>46</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-1571698723267541486</id><published>2010-11-30T16:11:00.003-05:00</published><updated>2010-11-30T16:28:52.852-05:00</updated><title type='text'>Pascal(a)'s Triangle</title><content type='html'>I've been toying with scala for a bit now.   Not as much as I'd like to, and not often for work, but just because I like what little I've seen.  As a bit of a college CS course throwback, I wrote a little scala script to print Pascal's triangle.  The goal of these exercises is to code in good scala style, using scala as best possible.  So, if you happen to be a scalawiz and see some un-scala construct, please point it out and suggest something more scala-ish.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;script src="https://gist.github.com/722438.js?file=Pascal.scala"&gt;&lt;/script&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/8840085855436806841-1571698723267541486?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/1571698723267541486/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=1571698723267541486' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/1571698723267541486'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/1571698723267541486'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2010/11/pascalas-triangle.html' title='Pascal(a)&apos;s Triangle'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-3926512098572716098</id><published>2010-02-13T14:09:00.003-05:00</published><updated>2010-02-13T14:33:26.516-05:00</updated><title type='text'>Flex Activate / Deactivate Events on OS X</title><content type='html'>In JamLegend, it would be nice to auto-pause the game when the user switches focus to another application.  You'd think it would be very simple to detect if Flash player currently has focus, but the recommended methods are unreliable cross-platform.  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Findings (so far)&lt;/b&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Chrome, Firefox, and Safari receive activate and deactivate events if the browser loads the SWF directly, but NOT if the SWF is loaded inside of an HTML page.&lt;/li&gt;&lt;li&gt;The method of loading the SWF is inconsequential; e.g. JavaScript vs. HTML tags.&lt;/li&gt;&lt;li&gt;When the SWF is loaded directly, activate is not dispatched when re-focusing on the browser by clicking the flex app; a second click is required.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;This is really a pain.  One suggested hack is to look for the MOUSE_LEAVE event (when the user moves the mouse off of the flex app).  That works well when UI elements are draggable and you need to handle the case of the mouse leaving the flex app while dragging, but it isn't a valid indicator of whether the flex app still has focus.  For example, in JamLegend, the user can move the mouse anywhere and they can still play as long as the flex app has focus (thus receiving key events).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There might be a big hack somewhere, utilizing activate and deactivate events for Windows, plus a combo of JavaScript focus events and mouse position for OS X, but I haven't figured it out yet...&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;System Details&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;OS X 10.6.2 &lt;/li&gt;&lt;li&gt;Flash Player 10,0,42,34 &lt;/li&gt;&lt;li&gt;Chrome 5.0.307.7 beta&lt;/li&gt;&lt;li&gt;Safari 4.0.4&lt;/li&gt;&lt;li&gt;Firefox 3.5.5.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Resources&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://blog.flexexamples.com/2008/02/29/determining-if-a-flex-application-has-focus-using-the-activate-and-deactivate-events/"&gt;flexexamples.com - determining if a flex app has focus using activate / deactivate events&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/46915/how-can-i-detect-if-a-flex-app-looses-focus"&gt;stackoverflow.com - how can i detect if a flex app looses focus?&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://blog.flexmonkeypatches.com/2007/12/07/detecting-when-a-flex-application-loses-focus/"&gt;flexmonkeypatches.com - detecting when a flex app loses focus&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.moock.org/blog/archives/000252.html"&gt;moock.org - Event.DEACTIVATE doesn't work in all browsers&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-3926512098572716098?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/3926512098572716098/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=3926512098572716098' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3926512098572716098'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3926512098572716098'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2010/02/flex-activate-deactivate-events-on-os-x.html' title='Flex Activate / Deactivate Events on OS X'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-8299970625002468276</id><published>2010-01-27T20:32:00.006-05:00</published><updated>2010-02-13T14:07:45.204-05:00</updated><title type='text'>Flex Binding "Gotcha!"</title><content type='html'>Somewhere online, I saw a comment saying that binding in Flex is "evil magic" and should be avoided except in the simplest of cases.  Though I could see where the author was coming from in theory, it wasn't until today that I encountered this in practice.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In short, setting a bindable variable to a field of a bindable class binds the variable to the &lt;i&gt;value&lt;/i&gt; referenced by the field, &lt;b&gt;not&lt;/b&gt; to the &lt;i&gt;reference&lt;/i&gt; in the bindable class.  Get it?  The whole pass by value vs. pass by reference thing.  Here's an example (ignore the use of the default package):&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;Class A:&lt;/b&gt;&lt;/div&gt;&lt;pre&gt;package&lt;br /&gt;{&lt;br /&gt;[Bindable]&lt;br /&gt;public class A&lt;br /&gt;{&lt;br /&gt;  public var val : int;&lt;br /&gt;  public var b   : B;&lt;br /&gt;&lt;br /&gt;  public function A()&lt;br /&gt;  {&lt;br /&gt;    val = 0;&lt;br /&gt;    b   = new B(0);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;Class B:&lt;/b&gt;&lt;/div&gt;&lt;pre&gt;package&lt;br /&gt;{&lt;br /&gt;[Bindable]&lt;br /&gt;public class B&lt;br /&gt;{&lt;br /&gt;  public var val : int;&lt;/pre&gt;&lt;pre&gt;    public function B(v:int)&lt;br /&gt;  {&lt;br /&gt;    this.val = v;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;MXML Test:&lt;/b&gt;&lt;/div&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;br /&gt;&amp;lt;mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;mx:Button click="onClick()" label="Click Me" /&amp;gt;&lt;br /&gt;&amp;lt;mx:Text text="A: {myA.val}" /&amp;gt;&lt;br /&gt;&amp;lt;mx:Text text="B: {myB.val}" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;mx:Script&amp;gt;&lt;br /&gt;  &amp;lt;![CDATA[&lt;br /&gt;    [Bindable] var myA:A = new A();&lt;br /&gt;    [Bindable] var myB:B = myA.b;&lt;br /&gt;&lt;br /&gt;    function onClick() : void&lt;br /&gt;    {&lt;br /&gt;      myA.val += 1;&lt;br /&gt;      myA.b = new B(3);&lt;br /&gt;    }&lt;br /&gt;  ]]&gt;&lt;br /&gt;&amp;lt;/mx:Script&amp;gt;&lt;br /&gt;&amp;lt;/mx:Application&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;At launch, the text elements both have the value 0.  But, after one click of the button, what is the value of &lt;code&gt;myB.val&lt;/code&gt;?  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I don't think I'm alone on this, but maybe I am.  In any case, I expected &lt;code&gt;myB.val&lt;/code&gt; to be 3, because &lt;code&gt;myB&lt;/code&gt; was assigned to &lt;code&gt;myA.b&lt;/code&gt; and &lt;code&gt;A&lt;/code&gt; is a &lt;code&gt;[Bindable]&lt;/code&gt; class.  When I changed the value of &lt;code&gt;myA.b&lt;/code&gt;, I expected &lt;code&gt;myB&lt;/code&gt; to reference new instance of &lt;code&gt;B&lt;/code&gt; that &lt;code&gt;myA.b&lt;/code&gt; now referenced.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Turns out it doesn't.  After one click of the button, &lt;code&gt;myB.val == 0&lt;/code&gt;.  Why?  Because &lt;code&gt;myB&lt;/code&gt; is still bound to the original instance of &lt;code&gt;B&lt;/code&gt; referenced by &lt;code&gt;myA.b&lt;/code&gt; at the time &lt;code&gt;myB&lt;/code&gt; was assigned, rather than whatever instance of &lt;code&gt;B&lt;/code&gt; that &lt;code&gt;myA.b&lt;/code&gt; might reference in the future.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Bummer.  If only &lt;code&gt;myB&lt;/code&gt; was a pointer...&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-8299970625002468276?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/8299970625002468276/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=8299970625002468276' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/8299970625002468276'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/8299970625002468276'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2010/01/flex-binding-gotcha_27.html' title='Flex Binding &quot;Gotcha!&quot;'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-2355482508719354782</id><published>2009-10-29T20:36:00.004-04:00</published><updated>2009-10-29T20:50:24.239-04:00</updated><title type='text'>Toggle Hibernate "Show SQL" at Runtime</title><content type='html'>Okay, the title is a bit deceiving.  As far as I know, you cannot modify the value of the configuration property "hibernate.show_sql."  However, that does not prohibit you from toggling at runtime whether SQL statement are output to your log files.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Why would you want to do this?  &lt;/b&gt;&lt;/div&gt;&lt;div&gt;Because turning on "show_sql" will flood your logs with all the SQL generated by hibernate, making it difficult to find a particular section you might be debugging or optimizing.  Being able to toggle the output of SQL allows you to enable it just for the sections of code you are investigating.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;First, the solution, then I'll get to why:&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;import org.apache.log4j.*;&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;Logger sqlLogger = Logger.getLogger("org.hibernate.SQL");&lt;/code&gt;&lt;/div&gt;&lt;code&gt;&lt;/code&gt;&lt;div&gt;&lt;code&gt;sqlLogger.setLevel(Level.DEBUG);&lt;br /&gt;&lt;br /&gt;... do your stuff ...&lt;br /&gt;&lt;br /&gt;sqlLogger.setLevel(Level.OFF);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;So what's going on here?&lt;/b&gt;&lt;/div&gt;&lt;div&gt;The key is knowing what "show_sql" does and that it is not the only way to log the SQL statements generated by hibernate.  Namely, "show_sql" sends all SQL to the standard output, but hibernate also uses Apache Commons Logging.  So, as long as you configure commons logging to use an underlying logging framework that supports changing log levels at runtime, you can toggle the output of SQL by getting a hold of the "org.hibernate.SQL" logger and bending it to your will.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-2355482508719354782?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/2355482508719354782/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=2355482508719354782' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/2355482508719354782'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/2355482508719354782'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2009/10/toggle-hibernate-show-sql-at-runtime.html' title='Toggle Hibernate &quot;Show SQL&quot; at Runtime'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-3012066697383018505</id><published>2009-04-10T21:57:00.002-04:00</published><updated>2009-04-10T22:08:09.269-04:00</updated><title type='text'>FragmentCache part duex: Cleaning</title><content type='html'>I realized that I forgot to address an additional, critical, issue with fragment caching: cleaning out expired fragments.  A fragment cache implemented as I described in my last post would update expired fragments when requested, but what about expired fragments that aren't requested?  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If you externalized the cache itself, then your cache will likely support LRU or LFU expiration policies. But, if you're using a in-memory cache in the Java heap space, well, that's a memory leak.  If you cache a lot of fragments, failing to clean the cache is a recipe for maxing out the old generation space and thus causing Full GC's to run back-to-back, collecting just enough space to limp along, but not do any good, until eventually your application becomes non-responsive.  So, an additional requirement for an in-memory fragment cache is that you monitor the usage and have a cleaner task which runs, say, once an hour, to remove fragments that have expired and are not currently locked for update.  Doing this efficiently with a large cache requires using a data structure that supports thread-safe concurrent access, so as to avoid locking the whole cache while cleaning and maintain high throughput.  &lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-3012066697383018505?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/3012066697383018505/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=3012066697383018505' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3012066697383018505'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3012066697383018505'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2009/04/fragmentcache-part-duex-cleaning.html' title='FragmentCache part duex: Cleaning'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-1802786844956378039</id><published>2009-04-05T21:10:00.006-04:00</published><updated>2009-04-06T04:40:41.618-04:00</updated><title type='text'>The JamLegend Fragment Cache</title><content type='html'>&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Abstract&lt;/span&gt;&lt;/div&gt;The benefits of fragment caching are pretty well-known.  If you haven't come across it before, the concept is simple:  cache portions of the page to lighten database load by reducing or eliminating redundant queries.  Often, this can be the "most bang for your buck" type of cache. How long a fragment should be cached is a function of the expense of generation, frequency of changes, and the frequency of access.  Ideal fragments for caching are the most accessed and most expensive to generate.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The fragment cache I'm going to discuss is the implementation of the FragmentCacheTag JSP custom tag, in J2Free, the one-day-to-be-open-source framework on which JamLegend is built (you can find more info on J2Free &lt;a href="http://j2free.org/"&gt;here&lt;/a&gt;, and in a few of my previous blogs).  &lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;JamLegend Examples&lt;/span&gt;&lt;/div&gt;&lt;div&gt;The "Popular Today" songs lists on JamLegend are a little expensive to generate, shown on all the highest trafficked pages, and don't change too quickly.  As such, we cache that list for 5 minutes, saving us a lot of queries but keeping the list rather dynamic.  On the other extreme, comments change constantly and need to be much more dynamic.  We cache comment threads for 30 seconds.  When you have a high number of concurrent users, even a 30 second cache can save you a lot of queries without sacrificing liveliness.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;The Code Samples&lt;/span&gt;&lt;/div&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;cache:fragment timeout="${30 * 1000}"&amp;gt;&lt;br /&gt;Simple cache based on a timeout of 30 seconds.&lt;br /&gt;&amp;lt;/cache:fragment&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;cache:fragment timeout="-1" condition="${size(items)}"&amp;gt;&lt;br /&gt;Simple cache with no timeout, using instead an additional attribute to refresh the cache when the size of the collection "items" has changed since the cache was last updated.  This works well in situations where data changes rarely, and you want it to be available immediately upon change.&lt;br /&gt;&amp;lt;/cache:fragment&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;cache:fragment timeout="${30*1000}" disable="${devMode}"&amp;gt;&lt;br /&gt;Simple cache based on a timeout of 30 seconds but with an attribute to disable the cache if devMode is flagged.  Here, devMode is a application scope attribute set when the server starts up. It's nice to be able to selectively disable certain caches for testing, and have them enabled live without having to change any code.&lt;br /&gt;&amp;lt;/cache:fragment&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The JamLegend FragmentCacheTag also supports an additional application scope attribute to globally disable all caching.  One of these days, I'm going to get around to changing the timeout to be specified in seconds, instead of milliseconds; just hasn't happened yet... &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Fragment Caching "Gotchas"&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Two of the first problems you'll run into with fragment caching are:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;logged in vs. logged out users&lt;/li&gt;&lt;li&gt;concurrent cache refreshing&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;The first is a problem you're going to encounter simply by using a fragment cache.  The second, on the other hand, you'll only run into if you're building your own fragment cache; if you use the J2Free FragmentCacheTag described here, it will be taken care of for you.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;Adding Dynamic Capabilities to Cached HTML&lt;/span&gt; &lt;br /&gt;&lt;/div&gt;&lt;div&gt;Sometimes ideal candidates for fragment caching have display differences when users are logged in.  For example, caching comment threads:  logged in users see a "report" link, whereas logged out users do not.  Since a fragment cache holds the generated HTML, as opposed to an object cache or query cache, it isn't easy or efficient to modify the fragment based on logged-in status (though, it's not impossible and I'd appreciate any elegant approaches).  Fortunately, JavaScript comes to the rescue.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;On JamLegend, a "report" link is included in the generated HTML for comment threads whether the user is logged in or not, with &lt;code&gt;style="display:none;"&lt;/code&gt; to hide it initially.  Then, if the user is logged in, we use a little JavaScript on page load to show all the report links, otherwise they stay hidden.  This is the same technique used by 37Signals (see &lt;a href="http://www.37signals.com/svn/posts/1557-javascript-makes-relative-times-compatible-with-caching"&gt;JavaScript Makes Relative Times Compatible with Caching&lt;/a&gt;).  Since we also check server-side that a user is logged in before allowing them to mark a comment as spam, there isn't a significant downside to having the hidden spam links for users who are not logged in.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Preventing Concurrent Refreshes&lt;/span&gt;&lt;/div&gt;&lt;div&gt;If you're caching a fragment because it's expensive to generate, the last thing you want is a few hundred users concurrently refreshing the fragment triggering a few hundred concurrent expensive queries.  So, how do you prevent this?  The first question you have to answer is this: &lt;blockquote&gt;When user A has triggered a cache refresh, what should happen to other users who hit the same fragment?&lt;/blockquote&gt;Should they: &lt;blockquote&gt;(a) Head over to user A's thread, wait for user A's thread to complete the query, and then ask for the result?  or,&lt;/blockquote&gt;&lt;blockquote&gt;(b) Should they just see that user A is refreshing the cache and so grab the last cached fragment and return immediately?&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;Both are acceptable answers, but which you choose depends on how stale you allow your fragment to be.  In case A, you can serve the most recent fragment to an unlimited number of callers while still only generating the fragment once.  Since user A triggered the refresh first, that thread is necessarily ahead of the other threads, so the others will still return is less time than if they were the sole thread refreshing the cache.  In case B, the subsequent threads can return immediately serving the last version of the fragment while assuming that user A's thread will refresh the cache.&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For J2Free's FragmentCacheTag, I chose scenario B on the reasoning that the fragment is cached to begin with because it is not crucial that it be 100% up-to-date.  Ergo, what's one more minute with the old fragment?  At some point, I want to add this as an optional attribute, something to the effect of:&lt;/div&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;cache:fragment timeout="600000" waitOnRefresh="true"&amp;gt;&lt;br /&gt;... where waitOnRefresh enabled would indicate that the cache should not server the last fragment while being refreshed, but rather attach itself to the thread conducting the refresh and get the result when complete.&lt;br /&gt;&amp;lt;/cache:fragment&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Obviously, the above only applies to multi-threaded servers.  There is an additional case, though, that applies to single and multi-threaded servers:  what happens in a cluster?  How do you prevent concurrent refreshes when you're running multiple servers?  A few options are: &lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Store fragments in a distributed cache, like ehcache, JBoss cache, or memcached, using replication instead of invalidation.  However, there is still the possibility of instance A triggering a refresh then instance B triggering a refresh before instance B received the updated fragment from instance A.  This window could be decreased by replicating a lock-for-update on a fragment, rather than waiting for the complete fragment, but not closed entirely.&lt;/li&gt;&lt;li&gt;Using a central, synchronized cache would remove the race-condition from situation 1, but at the cost of decreased throughput.  Depending on the location and network access to such a cache, it may or may not be worth it.  Ping times intra-ec2-zone are only 1-2ms between instances, so it might be worth it to add a few ms to each request to avoid concurrently executing an expensive query.&lt;/li&gt;&lt;li&gt;Use a distributed cache, as in situation #1, but only allow 1 instance to refresh the cache.  This may or may not work depending on how well you split requests between instances.  You certainly wouldn't want users on instance B to continue to receive stale data because no user on instance A has triggered a cache refresh.  This strategy may work well for pages that are guaranteed to be accessed on each instance.&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;I welcome comments on any additional solutions to preventing concurrent cache refreshing in a cluster.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-1802786844956378039?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/1802786844956378039/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=1802786844956378039' title='35 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/1802786844956378039'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/1802786844956378039'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2009/04/jamlegend-fragment-cache.html' title='The JamLegend Fragment Cache'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>35</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-7920480953356746731</id><published>2008-11-20T03:26:00.004-05:00</published><updated>2008-11-20T04:00:37.350-05:00</updated><title type='text'>Another Flex Cross-Browser Incompatibility (OS X ProgressBar display)</title><content type='html'>So, Flash is supposed to solve all our cross-browser issues... only it &lt;span class="Apple-style-span" style="font-style: italic;"&gt;doesn't&lt;/span&gt;.  My most recent discovery relates to the visual look of a &lt;code&gt;ProgressBar&lt;/code&gt; between OS X and Windows when the &lt;code&gt;mode = ProgressBarMode.MANUAL&lt;/code&gt; and &lt;code&gt;indeterminate = true&lt;/code&gt;.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To illustrate this, we'll start with the following MXML component:&lt;/div&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;mx:progressbar x="10" y="10" id="progress" labelplacement="bottom" indeterminate="true" enabled="true" minimum="0" maximum="100" label="" width="210"/&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;With some styling applied, the result is this:&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_PhxljBBvj_U/SSUgvFiJgWI/AAAAAAAAADE/p0-yHN0YWTs/s1600-h/Picture+2.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 326px; height: 195px;" src="http://1.bp.blogspot.com/_PhxljBBvj_U/SSUgvFiJgWI/AAAAAAAAADE/p0-yHN0YWTs/s400/Picture+2.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5270654932218380642" /&gt;&lt;/a&gt;&lt;br /&gt;Now, let's say that we want to use the &lt;code&gt;ProgressBar&lt;/code&gt; for some non-indeterminate (er, determinate) purpose, like loading some data from a server.  So, let's do this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;progress.indeterminate = false;&lt;br /&gt;progress.mode = ProgressBarMode.MANUAL;&lt;br /&gt;progress.setProgress(0,100);&lt;br /&gt;&lt;br /&gt;someLoader.addEventListener(ProgressEvent.PROGRESS,someProgressListener);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Great.  Now, &lt;code&gt;someLoader&lt;/code&gt; will load something, and &lt;code&gt;someProgressListener&lt;/code&gt; will keep us up-to-date about the progress.  So, say we've loaded all the data and now we're doing some processing, the progress of which we don't know since our processing function is synchronous and does not dispatch &lt;code&gt;ProgressEvent&lt;/code&gt;s.  So, we want to set &lt;code&gt;indeterminate&lt;/code&gt; back to true so that our users will know something is still happening but not show possibly incorrect progress with a determinate &lt;code&gt;ProgressBar&lt;/code&gt;. So, let's do this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;progress.removeEventListener(ProgressEvent.PROGRESS,someProgressListener);&lt;br /&gt;progress.indeterminate = true;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;WTF!&lt;/span&gt; Now our &lt;code&gt;ProgressBar&lt;/code&gt; looks like this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_PhxljBBvj_U/SSUi0YsDehI/AAAAAAAAADM/lonv2iJooZ8/s1600-h/Picture+1.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 370px; height: 210px;" src="http://4.bp.blogspot.com/_PhxljBBvj_U/SSUi0YsDehI/AAAAAAAAADM/lonv2iJooZ8/s400/Picture+1.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5270657222282803730" /&gt;&lt;/a&gt;&lt;br /&gt;In Firefox and IE on Windows, the &lt;code&gt;ProgressBar&lt;/code&gt; looks identical to before, which is the &lt;span class="Apple-style-span" style="font-style: italic;"&gt;expected&lt;/span&gt; behavior.  But in Safari (at least) on OS X, we have this monstrosity.  What happened?  Shouldn't Flash look identical in any browser?  Well, apparently it doesn't.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Fortunately, this isn't unfixable.  In fact, it's a very simple fix, though it makes zero sense to me &lt;span class="Apple-style-span" style="font-style: italic;"&gt;why&lt;/span&gt; this changes the visual display.  Just set &lt;code&gt;progress.mode = ProgressBarMode.EVENT&lt;/code&gt;.  Turns out that this ugly displacement only occurs in OS X &lt;code&gt;if (progress.mode == ProgressBarMode.MANUAL &amp;amp;&amp;amp; progress.indeterminate)&lt;/code&gt;.  Oh well, something to chalk up on the list of cross-browser differences in Flash.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;On another note, remember that &lt;code&gt;progress.setProgress(num,total)&lt;/code&gt; will not do anything unless you have set &lt;code&gt;progress.mode = ProgressBarMode.MANUAL&lt;/code&gt;.  This is another thing I think is ridiculous, but the &lt;code&gt;ProgressBar&lt;/code&gt; will ignore any calls to &lt;code&gt;setProgress&lt;/code&gt; if &lt;code&gt;mode != ProgressBarMode.MANUAL&lt;/code&gt;.  I understand that the different &lt;code&gt;ProgressBarMode&lt;/code&gt; settings will instruct the &lt;code&gt;ProgressBar&lt;/code&gt; where to expect progress changes from, but it still seems odd to fully ignore calls to &lt;code&gt;setProgress&lt;/code&gt; &lt;span class="Apple-style-span" style="font-weight: bold;"&gt;WITHOUT throwing any kind of error&lt;/span&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt; or warning&lt;/span&gt; when &lt;code&gt;mode != ProgressBarMode.MANUAL&lt;/code&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Versions:  This was discovered in OS X 10.5, Flash MAC 10.0.12.36 (debug), Safari 3.2.1.&lt;/div&gt;&lt;code&gt;&lt;code&gt;&lt;/code&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-7920480953356746731?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/7920480953356746731/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=7920480953356746731' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/7920480953356746731'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/7920480953356746731'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/11/another-flex-cross-browser.html' title='Another Flex Cross-Browser Incompatibility (OS X ProgressBar display)'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_PhxljBBvj_U/SSUgvFiJgWI/AAAAAAAAADE/p0-yHN0YWTs/s72-c/Picture+2.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-2182039916907354112</id><published>2008-11-10T04:34:00.002-05:00</published><updated>2008-11-10T04:51:55.969-05:00</updated><title type='text'>Flex / AS3 Tweening</title><content type='html'>I can't believe I haven't given a shout out to TweenLite before.  I love it.  Absolutely, love it.  For the only reason you should need to use TweenLite over Flash's built in Tween, Tweener, ZigoEngine (Fuse), Twease, HydroTween, or gTween, see &lt;a href="http://blog.greensock.com/tweening-speed-test/"&gt;this speed comparison&lt;/a&gt;.  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The second great part about TweenLite is that it is dead simple to use.&lt;/div&gt;&lt;code&gt;&lt;br /&gt;TweenLite.to(objectToTween, howLongToTween, { propsToTween ... callbacks });&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;div&gt;Ridiculously simple to integrate into any project.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I only have one complaint:  When I haven't tweened in a few days, I &lt;span class="Apple-style-span" style="font-style: italic;"&gt;always&lt;/span&gt; forget that the duration for a TweenLite is represented in secconds, not milliseconds.  Usually, it takes me about 5 minutes to remember, since my Tween didn't happen when I wanted and instead happened 300 seconds later as I was staring at my screen wondering what the hell I messed up.  I get that it's a convenience thing, but to me it's inconvenient.  If TweenLite durations were specified in milliseconds, I think I'd gain 1000* 60 * 5 * 7 back to my week.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-2182039916907354112?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/2182039916907354112/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=2182039916907354112' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/2182039916907354112'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/2182039916907354112'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/11/flex-as3-tweening.html' title='Flex / AS3 Tweening'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-2429344826277616630</id><published>2008-11-01T20:50:00.005-04:00</published><updated>2010-03-02T13:59:01.980-05:00</updated><title type='text'>Monitoring AWS Ubuntu Instances using Munin</title><content type='html'>&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Another munin setup blog? Why?&lt;/span&gt;&lt;/div&gt;&lt;div&gt;There are way too many blog posts out there with incorrect info on how to setup munin.  My process to get it setup infuriated me enough to break from work and write the post.  If any blog tells you to run:&lt;br /&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;code&gt;/usr/bin/munin-cron --force-root&lt;/code&gt;&lt;/blockquote&gt;&lt;div&gt;or any other munin command using &lt;code&gt;--force-root&lt;/code&gt;, you should instead run like hell in the opposite direction.  Doing so will only cause you hours of hell figuring out why your graphs stopped updating.  It turns out that by using &lt;code&gt;--force-root&lt;/code&gt; you just hijacked ownership of the .rrd, .png, and .html files that munin uses; for more details, see &lt;a href="http://munin.projects.linpro.no/wiki/faq#Q.Thegraphsarenotupdatinganymore"&gt;here&lt;/a&gt;.  Furthermore, anybody that wrote a blog telling you to do that clearly didn't wait to see whether their setup was successful before posting (or they made the unwise choice of running munin as root all the time).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There also is some bad information on the web about using the setting &lt;code&gt;use_node_name&lt;/code&gt; when setting up groups and hosts in the munin master config file.  Nearly every blog I found included enabling &lt;code&gt;use_node_name&lt;/code&gt; despite the munin documentation saying very specifically, &lt;blockquote&gt;"You will almost never want to set [use_node_name]  to anything, as setting it to "yes" tends to break things." (&lt;a href="http://munin.projects.linpro.no/wiki/munin.conf"&gt;source&lt;/a&gt;)&lt;/blockquote&gt;Doesn't that just &lt;span class="Apple-style-span" style="font-style: italic;"&gt;scream&lt;/span&gt; "Don't do it!"  And yet nearly everybody is blindly recommending it.  The thing is that by enabling it, you can make a setup work where it otherwise wouldn't have if you screwed up the config by using inconsistent host names between the munin master and munin nodes.  So, what should you do?  Is it okay to enable &lt;code&gt;use_host_name&lt;/code&gt;?  My inclination is no, because it's just a naive hack around the fact that you screwed up the config.  If the people that build munin say don't use it, then I say don't use it;  just learn how to configure munin correctly.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So, without further introduction, here's a more comprehensive guide to setting up a single munin master to monitor multiple munin nodes, and since I did this on AWS, there are some snippets about do's and don'ts on AWS.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;On the Munin Master&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Quite simple, run:&lt;/div&gt;&lt;blockquote&gt;&lt;code&gt;apt-get install -y munin apache2&lt;/code&gt;&lt;/blockquote&gt;&lt;div&gt;There.  Munin master is installed.  Easy, no?  That just setup the appropriate directories, cron jobs, and permissions, so all you really need to do next is edit &lt;code&gt;/etc/munin/munin.conf&lt;/code&gt; but we'll wait to do that until after the nodes are ready.  Additionally, it installed apache so that you can view munin reports on the web.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;On all Munin Nodes&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Again, simple, run:&lt;/div&gt;&lt;blockquote&gt;&lt;code&gt;apt-get install -y munin-node&lt;/code&gt;&lt;/blockquote&gt;&lt;div&gt;Told you that would be easy.  Great, now time to configure each node.  There are only a few things we need to configure on the node, and they are all located in &lt;code&gt;/etc/munin/munin-node.conf&lt;/code&gt;.  The properties to set are:&lt;/div&gt;&lt;blockquote&gt;&lt;code&gt;host_name my.hostname.com&lt;br /&gt;allow ^120\.0\.0\.1$&lt;/code&gt;&lt;/blockquote&gt;&lt;div&gt;The &lt;code&gt;host_name&lt;/code&gt; setting will be the name by which your munin-node will report itself to the munin master.  This is &lt;span class="Apple-style-span" style="font-style: italic;"&gt;very&lt;/span&gt; important, though I didn't find it mentioned in any other blog, for reasons I will explain below when I show the master &lt;code&gt;munin.conf&lt;/code&gt; config file.  The short version is that it doesn't really matter what you call the node, just choose something that makes sense to you and remember it.  For a database layer, something rational might be &lt;code&gt;mysql.yourdomain.com&lt;/code&gt; for the master, and &lt;code&gt;slave0.yourdomain.com&lt;/code&gt; and &lt;code&gt;slave1.yourdomain.com&lt;/code&gt; for the slaves.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The &lt;code&gt;allow&lt;/code&gt; setting specifies what master host(s) are allowed to connect to this munin node.  Due to some perl specifics, this value has to be a regex, though that can come in handy.  You can repeat the &lt;code&gt;allow&lt;/code&gt; line as much as necessary.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is where the AWS issue comes in:  what value do we put for allow?  Do we use the external address, such as &lt;code&gt;ec2-127-0-0-1.compute-1.amazonaws.com&lt;/code&gt;?  Do we use the internal address, &lt;code&gt;domU-00-00-00-00-00-00.compute-1.internal&lt;/code&gt;?  Or, do we use the ip address directly, say &lt;code&gt;127.0.0.1&lt;/code&gt;?  This will resurface later, too, in how the master will connect to the nodes.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Which one you choose does matter.  For example, if you use the ip address or the external AWS address, your AWS account will be charged for all data transfer and you will have to authorize port 4949 to allow the munin master to get through the firewall.  Ergo, it really makes sense to use the internal representation.  Furthermore, this allows you to keep the firewall closed, in which case you can put a nice little regex in the &lt;code&gt;allow&lt;/code&gt; line to allow any of your instances to connect, thus saving yourself headache if your master external address changes, if you decide to switch the master to a different address, or for some reason add another master.  So, on AWS, the easiest allow line entry is:&lt;/div&gt;&lt;blockquote&gt;&lt;code&gt;allow ^.*$&lt;/code&gt;&lt;/blockquote&gt;&lt;div&gt;If you're not familiar with regex, that literally allows any munin master to connect, which means that it would be ridiculously stupid to do in any situation where your machines are not all behind a common firewall.  Make sure to restart each munin-node (&lt;code&gt;/etc/init.d/munin-node restart&lt;/code&gt;) after editing the config file to ensure the changes are live.  Okay, now repeat that on as many machines/instances as you want to monitor.  For more options configuring a node, see the &lt;a href="http://munin.projects.linpro.no/wiki/munin-node.conf"&gt;munin documentation&lt;/a&gt;.  When finished, continue.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Configuring the Munin Master&lt;/span&gt;&lt;/div&gt;&lt;div&gt;The config file for the munin master is &lt;code&gt;/etc/munin/munin.conf&lt;/code&gt;.  The basic idea here is to define groups and hosts.  A simple group looks like:&lt;blockquote&gt;&lt;code&gt;&lt;br /&gt;[db.yourdomain.com]&lt;br /&gt;     address domU-00-00-00-00-00-00.compute-1.internal&lt;br /&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;div&gt;And here's what that all means:  You defined a group "yourdomain.com" and then the host "db.yourdomain.com" under that group.  Then, you said that the munin node located at that address is the host named db.yourdomain.com in the group yourdomain.com.  So, what is all this group nonsense?  The simple version is that it's just how hosts are organized, and that makes for a nice display on the web.  If you want to get more complex, you can configure different behaviors for groups or show totals by group.  For more info on this file, see the &lt;a href="http://munin.projects.linpro.no/wiki/munin.conf"&gt;munin documentation&lt;/a&gt;.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So, on to things that matter.  The name of the host (db.yourdomain.com) &lt;span class="Apple-style-span" style="font-weight: bold;"&gt;&lt;span class="Apple-style-span" style="font-style: italic;"&gt;MUST&lt;/span&gt;&lt;/span&gt; match the value of &lt;code&gt;host_name&lt;/code&gt; that the munin node at the address specified will report.  If it doesn't, then you won't see any data collected for the node.  The exception is if you enable &lt;code&gt;use_node_name&lt;/code&gt;, then graphs will show up because that setting tells munin to fetch "&lt;span class="Apple-style-span" style="font-style: italic;"&gt;all configured plugins associated with the given node&lt;/span&gt;, both plugins that report the nodes host_name and none at all," (&lt;a href="http://munin.projects.linpro.no/wiki/munin.conf"&gt;source&lt;/a&gt;).  But, since &lt;code&gt;use_node_name&lt;/code&gt; is not recommended, just make sure that the values of &lt;code&gt;host_name&lt;/code&gt; in munin-node.conf an the name of the host in munin.conf are the same. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Now Wait&lt;/span&gt;&lt;/div&gt;&lt;div&gt;You only have to wait 5 minutes at the most.  After that, you will be able to see the data collected from the web.  This is where a lot of blogs get impatient and tell you to run:&lt;blockquote&gt;&lt;code&gt;/usr/bin/munin-cron --force-root&lt;/code&gt;&lt;/blockquote&gt;which will update munin databases right away, but will also &lt;span class="Apple-style-span" style="font-style: italic;"&gt;hijack the permissions&lt;/span&gt; from user "munin" to the root user, thereby denying the munin user access to those files at a later time, which will stop all updates because the cron job installed in &lt;code&gt;/etc/cron.d/munin&lt;/code&gt; runs &lt;code&gt;/usr/bin/munin-cron&lt;/code&gt; as munin, not root.  You know, if I recommended doing that in my blog, I'd feel like a jerk for leading people astray.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Okay, patience is not one of your virtues and you don't want to wait 5 minutes?  Fine.  Just do this instead:&lt;/div&gt;&lt;blockquote&gt;&lt;code&gt;&lt;br /&gt;root@xxx:/root$ su munin --shell=/bin/bash&lt;br /&gt;munin@xxx:/root$ /usr/bin/munin-cron&lt;br /&gt;&lt;/code&gt;&lt;/blockquote&gt;&lt;div&gt;There, that will let you run munin-cron as "munin", which lets you see your update right away without hijacking file permissions.  Just hit ctrl+d when you're done to go back to whatever user you previously were.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Alright, hope that saves somebody some frustration.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-2429344826277616630?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/2429344826277616630/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=2429344826277616630' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/2429344826277616630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/2429344826277616630'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/11/monitoring-aws-ubuntu-instances-using.html' title='Monitoring AWS Ubuntu Instances using Munin'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-5144163312690491763</id><published>2008-10-21T01:20:00.004-04:00</published><updated>2008-10-21T01:23:29.102-04:00</updated><title type='text'>JamLegend Invites</title><content type='html'>So, it seems like there are a shortage of JamLegend invites on the web... and since I'm one of the Founder's, I figured I could give out a few here : )  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So, to sign up for JamLegend, head on over to &lt;a href="http://www.jamlegend.com/register/shinealight"&gt;http://www.jamlegend.com/register/shinealight&lt;/a&gt; and start jamming!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;p.s.  No, I'm not going to review my own product (duh, that's the rest of the world's job!).  I'm just handing out a few more invites (around 75 or so with that link).  And this is to basically explain where the hell I have been the last month and a half and why I haven't written anything....&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-5144163312690491763?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/5144163312690491763/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=5144163312690491763' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5144163312690491763'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5144163312690491763'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/10/jamlegend-invites.html' title='JamLegend Invites'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-6907157152079359071</id><published>2008-09-09T13:09:00.006-04:00</published><updated>2008-09-09T15:08:02.032-04:00</updated><title type='text'>JavaMail SMTP Authentication</title><content type='html'>There are a couple ways to authenticate yourself when using JavaMail APIs to send e-mail through an SMTP server.&lt;br /&gt;&lt;br /&gt;Here's one way:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public void sendMail(String fromAddress, &lt;br /&gt;                     String recipients, &lt;br /&gt;                     String subject, &lt;br /&gt;                     String content, &lt;br /&gt;                     String contentType, &lt;br /&gt;                     String smtpHost, &lt;br /&gt;                     int smtpPort, &lt;br /&gt;                     String username, &lt;br /&gt;                     String password) {&lt;br /&gt;  try {&lt;br /&gt;&lt;br /&gt;    Properties props = System.getProperties();&lt;br /&gt;    Session  session = Session.getDefaltInstance(props,null);&lt;br /&gt;&lt;br /&gt;    MimeMessage message = new MimeMessage(session);&lt;br /&gt;&lt;br /&gt;    message.setFrom(fromAddress);&lt;br /&gt;    message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipients, false));&lt;br /&gt;&lt;br /&gt;    message.setSubject(subject);&lt;br /&gt;    message.setContent(content,contentType);&lt;br /&gt;    message.setSentDate(new Date());&lt;br /&gt;&lt;br /&gt;    Transport transport = session.getTransport("smtp");&lt;br /&gt;    transport.connect(smtphost,smtpPort,username,password);&lt;br /&gt;    transport.sendMessage(message,message.getAllRecipients());&lt;br /&gt;    transport.close();&lt;br /&gt;&lt;br /&gt;  } catch (AddressException e) {&lt;br /&gt;    e.printStackTrace();&lt;br /&gt;  } catch (MessagingException e) {&lt;br /&gt;    e.printStackTrace();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;I don't like that way, because it's no good for anything but sending via an SMTP server with password based authentication.  So, if I want to send e-mail through an SMTP server that has, say, a white-listed IP policy so I don't need to provide authentication, I have to write a whole new function that will duplicate a significant part of that code.  Also, that long list of arguments to the function is just ugly, but it's what you're stuck with if you abstract a function that's hard-coded for SMTP password authentication.&lt;br /&gt;&lt;br /&gt;Here's a better way, that is abstracted nicely:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;public void sendMail(Properties props, &lt;br /&gt;                     Authenticator authenticator, &lt;br /&gt;                     String fromAddress, &lt;br /&gt;                     String recipients, &lt;br /&gt;                     String subject, &lt;br /&gt;                     String content, &lt;br /&gt;                     String contentType) {&lt;br /&gt;  try {&lt;br /&gt;&lt;br /&gt;    Session session = Session.getDefaultInstance(props, authenticator);&lt;br /&gt;&lt;br /&gt;    Message message = new MimeMessage(session);&lt;br /&gt;&lt;br /&gt;    message.setFrom(fromAddress);&lt;br /&gt;    message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipients, false));&lt;br /&gt;&lt;br /&gt;    message.setSubject(subject);&lt;br /&gt;    message.setContent(content,contentType);&lt;br /&gt;    message.setSentDate(new Date());&lt;br /&gt;&lt;br /&gt;    Transport.send(message);&lt;br /&gt;&lt;br /&gt;  } catch (AddressException e) {&lt;br /&gt;    e.printStackTrace();&lt;br /&gt;  } catch (MessagingException e) {&lt;br /&gt;    e.printStackTrace();&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The function above can be used to send e-mail with all sorts of configurations.  All you have to do is pass it the &lt;code&gt;Properties&lt;/code&gt; and, if you need to, an instance of a class extending &lt;code&gt;javax.mail.Authenticator&lt;/code&gt;.  Here's an example to send an e-mail identical to the first example above:&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;Properties props = System.getProperties();&lt;br /&gt;props.put("mail.smtp.host","smtpHost");&lt;br /&gt;props.put("mail.smtp.port","smtpPort");&lt;br /&gt;props.put("mail.smtp.auth","true");&lt;br /&gt;&lt;br /&gt;Authenticator auth = new Authenticator() {&lt;br /&gt;  public PasswordAuthentication getPasswordAuthentication() {&lt;br /&gt;    return new PasswordAuthentication("username","password");&lt;br /&gt;  }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;sendMail(props,auth,"no-reply@mysite.com","user@site.com","Hello World","Wow, big world.","text/plain");&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And, to send an e-mail without authentication (as in the IP white-list policy):&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;Properties props = System.getProperties();&lt;br /&gt;props.put("mail.smtp.host","smtpHost");&lt;br /&gt;props.put("mail.smtp.port","smtpPort");&lt;br /&gt;&lt;br /&gt;sendMail(props,null,"no-reply@mysite.com","user@site.com","Hello World","Wow, big world.","text/plain");&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The great part is that the function sending the e-mail doesn't need to know anything about how it's going to do it, since the configuration is entirely passed in as arguments via the session and the optional authenticator.  There is, however, a catch that I've noticed has tripped people up all over the internet.  The values you set to your &lt;code&gt;Properties&lt;/code&gt; object &lt;b&gt;must&lt;/b&gt; all be of class &lt;code&gt;String&lt;/code&gt;. For example, this works&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Properties props = System.getProperties();&lt;br /&gt;props.put("mail.smtp.host","mail.mysite.com");&lt;br /&gt;props.put("mail.smtp.port","587");&lt;br /&gt;props.put("mail.smtp.auth","true");&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Whereas, this does not:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Properties props = System.getProperties();&lt;br /&gt;props.put("mail.smtp.host","mail.mysite.com");&lt;br /&gt;props.put("mail.smtp.port",587);&lt;br /&gt;props.put("mail.smtp.auth",true);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;It doesn't matter that the values for &lt;code&gt;mail.smtp.port&lt;/code&gt; and &lt;code&gt;mail.smtp.auth&lt;/code&gt; are an &lt;code&gt;int&lt;/code&gt; and a &lt;code&gt;boolean&lt;/code&gt;, respectively.  If they are not specified as a &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Transport.sendMessage(message)&lt;/code&gt; will fail.&lt;br /&gt;&lt;br /&gt;* As a disclaimer, I typed the above code, rather than copy and paste from a program I've compiled, so I apologize if there's a typo anywhere up there that prevents it from compiling.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-6907157152079359071?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/6907157152079359071/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=6907157152079359071' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/6907157152079359071'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/6907157152079359071'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/09/javamail-smtp-authentication.html' title='JavaMail SMTP Authentication'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-598573565105558364</id><published>2008-09-07T01:50:00.004-04:00</published><updated>2010-01-22T15:31:40.648-05:00</updated><title type='text'>Socket Programming with Flex and Apache Mina (Part 2)</title><content type='html'>I was responding to &lt;a href="http://narup.blogspot.com/"&gt;narup&lt;/a&gt;'s comment on my previous post, &lt;a href="http://blog.augmentedfragments.com/2008/07/socket-programming-with-flex-and-apache.html"&gt;Socket Programming with Flex and Apache Mina&lt;/a&gt;, and my response got so long that I decided it was worth a follow-up post.  The previous post focused mainly on the parts involved in socket layer communications and some common pitfalls.  This post will focus more on decisions when defining a protocol to use in socket layer communications.  &lt;a href="http://narup.blogspot.com/"&gt;narup&lt;/a&gt; asked two things:&lt;div&gt;&lt;ol&gt;&lt;li&gt;Whether I used binary or XML sockets, and&lt;/li&gt;&lt;li&gt;Some general clues on the whole architecture&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;The most important thing is to define your protocol.&lt;/span&gt;  If you choose a binary protocol, some important decisions to make are,&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Whether to have a header and, if so, what it will contain&lt;/li&gt;&lt;li&gt;Whether a command will have a fixed or variable length and, if variable, how to know the length&lt;/li&gt;&lt;li&gt;Whether to have the header indicate the type of command and, if so, how many types you might implement, which will help determine how many bytes you allocate for the command.&lt;/li&gt;&lt;/ul&gt;I'm a strong believer that&lt;span class="Apple-style-span" style="font-style: italic;"&gt; it's impossible to anticipate every case you'll ever need&lt;/span&gt;, so I like to leave myself room to expand without having to change the protocol.  This inclines me toward variable length commands.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Command Length&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Reading a fixed length command is easy.  If your protocol is 8 bytes of data, you read 8 bytes.  Variable length commands, by their nature, are of an unpredictable length.  Ergo, you can't just read bytes blindly.  Each command must somehow transmit with it an indicator for the size of the command.  Some options are terminating with a null byte, a particular sequence of bytes (like an EOF character), or indicating the length before the command.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Terminating with a null byte can be problematic if for some reason you have a null byte in your command.  I don't want to rule out that possibility, so I rule out that strategy.  I also don't like reading ahead without any idea of how far I'm going, so I'm just not a big fan of terminating with a sequence.  This can be especially tricky when your command doesn't show up in its unique entirety, but rather comes in pieces or along with another command. Knowing how far to read by prefacing the command with byte or four indicating the length, however, is clean and simple.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I really like variable length commands because its easily expandable if you decide to 10x the size of the data on which you're operating, but it doesn't require every message to be that 10x size, especially if your data is 1/100th of that when you start out.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Headers&lt;/span&gt;&lt;/div&gt;&lt;div&gt;When you have the length prefacing the command, you might as well call it a header.  I like headers.  They separate operations from data.  I like sending a command with a header that indicates what to do and the length of the data upon which to act.  Thus, the command body contains only the data.  Simple.  But now we're back to the length issue again... is our header variable or fixed?  I actually like fixed header lengths, because otherwise you end up with the same prefacing or termination issues I just discussed for the body.  Since a header is more defined (specific to the protocol than the data) and less likely to undergo volatile changes as your development progresses (unless you change your protocol, in which case this is all out the window), it's easy to define a fixed length header with ample room for growth.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For example, if in all the planning I can only define 10 command types, I'm still likely to allocate a whole byte to specifying the command despite the fact that I could fit my 10 known types into 4 bits with 6 as yet unknown types to spare.  The fact of the matter is that 8 bits vs. 4 bits is a miniscule difference but if months later I needed 17 types, I certainly won't want to have to rewrite my encoders/decoders.  Same goes for the number of bytes to allocate toward indicating the length of the command; maybe you only need 2 bytes for now, but why not use a full integer just in case.  Those 2 additional bytes aren't going to break the bank.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;The Right Socket for Your Protocol&lt;/span&gt;&lt;br /&gt;This is a bit of a square-peg, round-hole point.  It's not usually that definite, but once you've defined your protocol, the decision about how to carry out the transmissions will mainly depend on how much you value human-readability vs. markup overhead, as well as what you're most comfortable with.&lt;br /&gt;&lt;br /&gt;I used binary, since the server would be relaying thousands of commands a second and I wanted to be as lightweight as possible.  That said, reading and writing byte arrays to process your command is a recipe for obscure bugs you'll spend hours tracking down.  By writing protocol encoders/decoders on both the mina and flex sides, and logic testing those independently of the system, I was able to handle the commands in an OO fashion within my programs, but still transmit the command in a super lightweight fashion.&lt;br /&gt;&lt;br /&gt;In my case, since I had confirmed the correctness of the encoder/decoder logic, I really didn't care at all about the human readability that XML would have provided.  Plus, the overhead of the markup was undesirable both in terms of network transfer and parsing time.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Additional Note&lt;/span&gt;&lt;br /&gt;I should probably put a disclaimer that I also prefer sending and receiving JSON over XHR instead of XML, as well as properties files instead of XML config files, haha.  I did use XML sockets when writing a Mina-based server to act solely as a policy file server, though, and I could think of cases where it makes sense.&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/8840085855436806841-598573565105558364?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/598573565105558364/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=598573565105558364' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/598573565105558364'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/598573565105558364'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/09/socket-programming-with-flex-and-apache.html' title='Socket Programming with Flex and Apache Mina (Part 2)'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-1168629334958976161</id><published>2008-09-05T17:01:00.004-04:00</published><updated>2008-09-05T17:13:36.098-04:00</updated><title type='text'>Connecting to EC2 from iSSH on the iPhone</title><content type='html'>I've been debating buying an SSH app for my iPhone for some time, but wanted to wait until I found one that supported key based authentication so I could connect to EC2 instances.  After careful evaluation, I decided that iSSH looked like the best one to try out, and so I purchased it from the iPhone app store yesterday.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;iSSH has a great little feature to generate it's own key and then transfer that key to any machine you want, but it requires that you know the password for that machine.  When it comes to EC2 instances, I've never used a password and to my knowledge, there isn't a user account with a password on them.  Problem.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;After posting on the iSSH google group asking if there was going to be a way to transfer a key to iSSH, Chris Jones responded that I might be able to connect if I could associate multiple keys with my AWS account.  I looked into this and discovered that, no, Amazon does not let you associate multiple X.509 certificates with an AWS account.  However, in this process, I realized that there wasn't anything stopping me from adding iSSH's public key to the authorized_keys file on the machine I wanted to connect to.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here's the steps:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;If you don't have one already, install a SSH server on you computer.  I have a macbook pro running OS X 10.5, so all I had to do was enable remote login from the System Preferences, which enables the SSH server under the hood.&lt;/li&gt;&lt;li&gt;Use iSSH's transfer function to transfer the iSSH key to your computer.&lt;/li&gt;&lt;li&gt;Copy the iPhone iSSH key from ~/.ssh/authorized_keys (if you have multiple, it will be indicated with a comment following the key of the type "iphone-rsa-key-&amp;lt;SOME_NUMBER&amp;gt;&lt;some_number&gt;")&lt;/some_number&gt;&lt;/li&gt;&lt;li&gt;Add the key to the ~/.ssh/authorized_keys file of the EC2 instance you want to connect to.&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;br /&gt;Viola! Key-based authentication allowing iSSH to connect to an EC2 machine, or any machine for that matter.  If you chose to have iSSH use a password/passphrase when creating its' key, you'll have to enter this whenever you connect, but that's probably a good thing in case you lose your phone...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-1168629334958976161?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/1168629334958976161/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=1168629334958976161' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/1168629334958976161'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/1168629334958976161'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/09/connecting-to-ec2-from-issh-on-iphone.html' title='Connecting to EC2 from iSSH on the iPhone'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-2634976729674841058</id><published>2008-09-04T21:06:00.002-04:00</published><updated>2008-09-04T21:19:57.553-04:00</updated><title type='text'>Glassfish: Dynamic Reconfiguration</title><content type='html'>&lt;div&gt;There is an option in the Glassfish server config for "dynamic reconfiguration".   I've stumbled across many a forum post asking, &lt;blockquote&gt;Why does glassfish not reflect my configuration changes?  I have dynamic-reconfiguration enabled and I see the change reflected in the domain.xml, but the config changes aren't live!&lt;/blockquote&gt; The answer is simple, but not intuitive:  That's not what dynamic-reconfiguration means.  From the &lt;a href="http://wiki.glassfish.java.net/attach/GlassFishAdminReferences/monitoring-engineering.html#reconfig"&gt;engineering document&lt;/a&gt;, dynamic-reconfiguration is defined as the  &lt;blockquote&gt;Ability to be able to control the output of statistical data while the server is running.&lt;/blockquote&gt;Thus, dynamic-reconfiguration does not mean that Glassfish and the JVM are dynamically reconfigurable, but rather that monitoring levels can be changed without having to restart the server.  This especially useful if you want to enable monitoring when your server is bogged down to find out why, but don't want the overhead of monitoring running all the time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So, in short, love dynamic-reconfiguration for what it is, but don't think it's a catch all for changing anything and seeing the change right away.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-2634976729674841058?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/2634976729674841058/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=2634976729674841058' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/2634976729674841058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/2634976729674841058'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/09/glassfish-dynamic-reconfiguration.html' title='Glassfish: Dynamic Reconfiguration'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-524007560572447670</id><published>2008-09-04T02:24:00.008-04:00</published><updated>2008-09-04T03:10:34.788-04:00</updated><title type='text'>Sending Large Amazon SQS Messages with Typica in Java</title><content type='html'>Here's a real-world case I ran into building JamLegend (&lt;a href="http://jamlegend.com/"&gt;http://jamlegend.com&lt;/a&gt;)...&lt;br /&gt;&lt;br /&gt;Part of JamLegend is in the web-tier and part is in multiple socket-layer servers built on Apache Mina's awesome non-blocking technology.  When you have only one of either, communication is easy, but when you have multiple of each, then you really need a system to communicate effectively no matter how many of each, or which particular of each, happen to be running at any given moment in time.  Fortunately, &lt;a href="http://www.amazon.com/Simple-Queue-Service-home-page/b?ie=UTF8&amp;amp;node=13584001"&gt;Amazon SQS&lt;/a&gt; comes to the rescue and, combined with Typica (&lt;a href="http://code.google.com/p/typica/"&gt;http://code.google.com/p/typica/&lt;/a&gt;) makes it brilliantly simply to distribute jobs amongst any size group of workers.&lt;br /&gt;&lt;br /&gt;But SQS has it's own limitation. From the documentation, &lt;blockquote&gt;Amazon SQS messages can contain up to 8 KB of text data, including XML, JSON and unformatted text.&lt;/blockquote&gt; But that isn't the whole story.  More specifically, SQS messages can contain up to 8KB of UTF-16 encoded text data, which makes a big difference in the max character length of your message.  So, now we have a constraint to work with, but it's not hard to imagine a case where sending larger messages is quite useful.&lt;br /&gt;&lt;br /&gt;Ergo, a code tutorial on chunking SQS messages and some brief discussion of other considerations.&lt;br /&gt;&lt;br /&gt;I use &lt;a href="http://code.google.com/p/typica/"&gt;Typica&lt;/a&gt;, as a mentioned above, which is an awesome SQS utility for Java.  Assume for the following that your internal protocol for SQS messages is:  &lt;blockquote&gt;header;content&lt;/blockquote&gt; Given that, chunking a large SQS message into multiple smaller messages is pretty straightforward.  First, that header is going to need to accompany all of the messages.  Then, we'll repeatedly form messages of the maximum size until we've sent all the data.  In the following code, &lt;code&gt;message&lt;/code&gt; would be the raw text of the SQS message you wish to send.&lt;br /&gt;&lt;br /&gt;Some helpers:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;public static final String UTF_16 = "UTF-16";&lt;br /&gt;&lt;br /&gt;public static final int SQS_MAX_MESSAGE_SIZE = 8192;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now onto the logic:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;// Connect to the Queue&lt;br /&gt;MessageQueue queue = SQSUtils.connectToQueue(SQS_QUEUE_NAME,AWS_ACCESS_KEY_ID,AWS_SHARED_SECRET);&lt;br /&gt;&lt;br /&gt;// if message is small enough to be sent as one message, do it&lt;br /&gt;if (message.getBytes(UTF_16).length &lt;= SQS_MAX_MESSAGE_SIZE) {&lt;br /&gt;  queue.sendMessage(message);&lt;br /&gt;} else {&lt;br /&gt;                &lt;br /&gt;  // if it's too big for one message, chunk it and send as multiple&lt;br /&gt;&lt;br /&gt;  // split the message according to the protocol, but limit it&lt;br /&gt;  // in case the content portion contains a semi-colon&lt;br /&gt;  String[] parts = message.split(";",2);&lt;br /&gt;                &lt;br /&gt;  // break off the header, this is needed for each chunk&lt;br /&gt;  byte[] header = (parts[0] + ";").getBytes(UTF_16);&lt;br /&gt;                &lt;br /&gt;  // get the content as a byte[]&lt;br /&gt;  byte[] content = parts[1].getBytes(UTF_16);&lt;br /&gt;                &lt;br /&gt;  // figure out how much content can be in each chunk&lt;br /&gt;  int chunkSize = SQS_MAX_MESSAGE_SIZE - header.length;&lt;br /&gt;                &lt;br /&gt;  // create a byte[] for our max message size&lt;br /&gt;  // we're going to repeatedly fill this and send the message while&lt;br /&gt;  // content remains.&lt;br /&gt;  byte[] bytes = new byte[SQS_MAX_MESSAGE_SIZE];&lt;br /&gt;                &lt;br /&gt;  // copy the header into the byte[], we'll only do this once&lt;br /&gt;  System.arraycopy(header,0,bytes,0,header.length);&lt;br /&gt;                &lt;br /&gt;  // while there is content left, send a message&lt;br /&gt;  for (int i = 0; i &lt; content.length; i += chunkSize) {&lt;br /&gt;            &lt;br /&gt;  // copy the smaller of the remaining bytes or the max chunkSize chunk&lt;br /&gt;  // of content into the message array, then send the message.  Form the &lt;br /&gt;  // message String from the appropriate portion of the array&lt;br /&gt;  if (content.length - i &lt; chunkSize) {&lt;br /&gt;                        &lt;br /&gt;    System.arraycopy(content,i,bytes,header.length,content.length - i);&lt;br /&gt;    message = new String(bytes,0,header.length + content.length - i,UTF_16);&lt;br /&gt;                        &lt;br /&gt;  } else {&lt;br /&gt;                        &lt;br /&gt;    System.arraycopy(content,i,bytes,header.length,chunkSize);&lt;br /&gt;    message = new String(bytes,UTF_16);&lt;br /&gt;                        &lt;br /&gt;  }&lt;br /&gt;  // send the chunk&lt;br /&gt;  queue.sendMessage(message);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Since the max size is a measure of UTF-16 bytes, I found it easiest to just deal with everything as a byte[], but you could do it other ways if you felt like it.  The bigger thing to note is that this is really only half the solution, since now you face the task of handling chunked SQS messages on the other side.  Possible solutions include:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Ensuring each message is executable alone, and thus the desired effect is the sum of the executions of each individual message.  This has the benefit of not caring which of the workers receive the message, and was the solution I chose to implement.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ensure each message is received by a particular worker, which will then reassemble the messages and execute the whole.  For this to work, you really should implement an additional part to the header indicating how many chunks compose a whole and have each chunk identify itself as chunk x of y. That way, the worker will know when it has received the entirety of the message and may execute its task.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-524007560572447670?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/524007560572447670/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=524007560572447670' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/524007560572447670'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/524007560572447670'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/09/sending-large-amazon-sqs-messages-with.html' title='Sending Large Amazon SQS Messages with Typica in Java'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-7018959302184376783</id><published>2008-08-18T23:11:00.002-04:00</published><updated>2008-08-18T23:13:49.376-04:00</updated><title type='text'>JavaScript Scrolling with Prototype or Script.aculo.us</title><content type='html'>Just discovered that both Prototype's &lt;code&gt;Element.scrollTo('id')&lt;/code&gt;, or &lt;code&gt;$('id').scrollTo()&lt;/code&gt;, and Script.aculo.us' &lt;code&gt;Effect.ScrollTo('id')&lt;/code&gt; will fail if the desired element is not currently visible on the page.  I didn't try with &lt;code&gt;visibility:hidden;&lt;/code&gt; but trying to programmatically scroll to an element with &lt;code&gt;display:none;&lt;/code&gt; will result in nothing happening.  Just something to remember...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-7018959302184376783?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/7018959302184376783/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=7018959302184376783' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/7018959302184376783'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/7018959302184376783'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/08/javascript-scrolling-with-prototype-or.html' title='JavaScript Scrolling with Prototype or Script.aculo.us'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-7241475080540620106</id><published>2008-07-31T12:47:00.002-04:00</published><updated>2008-07-31T12:49:13.379-04:00</updated><title type='text'>JavaScript Date Formatting</title><content type='html'>Discovered &lt;a href="http://blog.stevenlevithan.com/archives/date-time-format"&gt;this awesome script&lt;/a&gt; for formatting dates in JavaScript.  I always wanted a DateFormat in JavaScript similar to the DateFormat in Java, and now I do; exciting discovery!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-7241475080540620106?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/7241475080540620106/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=7241475080540620106' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/7241475080540620106'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/7241475080540620106'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/07/javascript-date-formatting.html' title='JavaScript Date Formatting'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-8130042179174264961</id><published>2008-07-23T11:20:00.003-04:00</published><updated>2008-07-23T11:33:13.065-04:00</updated><title type='text'>Hibernate Bug when counting Extra Lazy Collections mapped with @Where</title><content type='html'>For a quick background, the &lt;code&gt;@Where&lt;/code&gt; annotation can be put on a Collection to specify additional SQL conditions.  The &lt;code&gt;@LazyCollection&lt;/code&gt; annotation can be used to specify when and how to load a collection.  For example, using &lt;code&gt;LazyCollectionOption.EXTRA&lt;/code&gt; tells hibernate to prefer extra queries to overfilling eagerly.  This means go beyond Hibernate's normal lazy loading and is useful when dealing with very large collections, since it will cause hibernate to execute &lt;code&gt;COUNT&lt;/code&gt; statements when accessing the size of an unloaded collection, rather than load the whole collection normally.  If you know that you frequently query the size of a large collection without needing any of the actual elements, this boosts performance.&lt;br /&gt;&lt;br /&gt;However, today I had an odd situation.  I have a collection that was mapped with both a &lt;code&gt;@Where&lt;/code&gt; annotation and &lt;code&gt;@LazyCollection(LazyCollectionOption.EXTRA)&lt;/code&gt;.  When querying the size of my collection, I was getting a result of 2, yet when I attempted to iterate the collection, the size was returning 0.  Excuse me?  How is that?&lt;br /&gt;&lt;br /&gt;Well, after enabling SQL logging and taking a look at what was going on, I discovered that when executing a &lt;code&gt;COUNT&lt;/code&gt; query as specified by &lt;code&gt;@LazyCollection(LazyCollectionOption.EXTRA)&lt;/code&gt;, Hibernate ignored the &lt;code&gt;@Where&lt;/code&gt; annotation.  When loading the elements of the collection, however, Hibernate correctly minded the &lt;code&gt;@Where&lt;/code&gt; annotation.  Since the &lt;code&gt;@Where&lt;/code&gt; condition I was specifying was actually significant, these two methods returned different results.&lt;br /&gt;&lt;br /&gt;Clearly, this isn't ideal behavior.  But, since it exists, I imagine that it doesn't come up often.  Also, for those paying close attention and asking why I have &lt;code&gt;LazyCollectionOption.EXTRA&lt;/code&gt; on any collection that has 2 items, the answer is because it's in early development right now and will not have only 2 items in practice.&lt;br /&gt;&lt;br /&gt;Off to file a Hibernate bug report....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-8130042179174264961?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/8130042179174264961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=8130042179174264961' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/8130042179174264961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/8130042179174264961'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/07/hibernate-bug-when-counting-extra-lazy.html' title='Hibernate Bug when counting Extra Lazy Collections mapped with @Where'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-6326046721719045627</id><published>2008-07-20T18:18:00.024-04:00</published><updated>2008-07-23T12:02:13.332-04:00</updated><title type='text'>Socket Programming with Flex and Apache Mina</title><content type='html'>Recently, I've been doing a significant amount of socket programming between a flex application and our Java servers.   Namely, we were building a game in Flex with enable real-time multiplayer.  Since HTTP connections are way too slow for real-time multiplayer games, we needed to use socket layer communications organized into channels.  Here's a couple things I learned in the process.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Version 1&lt;/span&gt;&lt;br /&gt;The first version I whipped together used standard TCP blocking sockets with pools of acceptor threads, worker threads, and SQS monitoring threads (since the multiplayer servers communicate with the web-application servers via Amazon SQS).  This was a straight forward process, with the main difficulty being to guaranteeing synchronized access to resources shared by multiple threads.  The result was more complex than desired and could handle a max load of only a few hundred concurrent users.  Beyond that, the CPU time would become bogged down by switching contexts between worker threads, leaving little CPU resources for actually doing the work.  Not good enough... scrap it, on to round 2.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Version 2&lt;/span&gt;&lt;br /&gt;The second version involved using Java NIO non-blocking socket connections, which promised potential scalability to thousands of concurrent users with very few threads, and thus very little context swapping and a lot of CPU resources for actual work.  The downside is that writing an NIO server is significantly more complicated and, in the words or a friend, really a pain in the ass.  Fortunately, I only spent a few hours designing how I wanted to proceed before the same friend saved me days of work pointed me in the direction of &lt;a href="http://mina.apache.org/"&gt;Apache Mina&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Version 3&lt;/span&gt;&lt;br /&gt;The third version was implemented with &lt;a href="http://mina.apache.org/"&gt;Apache Mina&lt;/a&gt; in roughly 2 days and has worked wonderfully thus far.  The &lt;a href="http://mina.apache.org/documentation.html"&gt;documentation for Mina&lt;/a&gt; is quite good, with plenty of sample applications. Mina is basically a barebones Java NIO server that can be configured to do whatever you need from a non-blocking server.  It handles all of the gritty Java NIO and low-level communication and provides the programmer with standard web-application conventions like Sessions and a filter chain.  Wow, that was awesome.&lt;br /&gt;&lt;br /&gt;Implementing a server based on Mina is done with three components:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;A protocol encoder&lt;/li&gt;&lt;li&gt;a protocol decoder&lt;/li&gt;&lt;li&gt;a protocol handler&lt;/li&gt;&lt;/ol&gt; The first two become part of the filter chain between Mina and the net, and handle the job of encoding/decoding your internal representation of a command to/from the bytes that are sent across the net. The beauty of this is that, internally, your command can be a POJO offering whatever convenience methods you desire, as opposed to the ByteBuffer it is received as.  The protocol handler is the class that does all of the work.  It includes such functions as &lt;code&gt;sessionCreated&lt;/code&gt;, &lt;code&gt;sessionIdle&lt;/code&gt;, &lt;code&gt;sessionClosed&lt;/code&gt;, and &lt;code&gt;messageReceived&lt;/code&gt; (which are called when their names would suggest).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;Overall, the Mina version was both the simplest in design and implementation, as well as the best performing.  I'm extremely happy with the choice.&lt;br /&gt;&lt;br /&gt;On the Flex side, I decided to mimic the Mina structure by implementing a protocol encoder/decoder as well as a handler, which has worked out quite well.  Since working with Mina had already involved thinking in that manner, porting the structure to Flex was the most natural procedure.&lt;br /&gt;&lt;br /&gt;So, basic intro aside, here are a few of the "gotchas" that I encountered:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Policy File Requests&lt;/span&gt;&lt;br /&gt;The first thing Flex will do when opening a Socket connection is send a policy file request to the host and port to which you are trying to conect.  You have two options in this scenario.   The first is to have your server check for &lt;code&gt;&amp;lt;policy-file-request /&amp;gt;&lt;/code&gt; and respond with the a policy file.  The second is to have Flex read the policy file from an alternate location via &lt;code&gt;Security.loadPolicyFile("wherever")&lt;/code&gt; before attempting to open the socket connection.  I found the later to be more convenient since it removed the need for my mulitplayer server to know how to handle policy file requests.  There are a few restrictions with this route, though.  &lt;div&gt;&lt;br /&gt;&lt;div&gt;See &lt;a href="http://livedocs.adobe.com/flex/3/langref/flash/system/Security.html#loadPolicyFile%28%29"&gt;here&lt;/a&gt; for the full reference, but the basic rules are these:  &lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;A policy file served from above port 1024 cannot grant access to a port below 1024&lt;/li&gt;&lt;li&gt;Policy files served via an xmlsocket (rather than an Http request to an actual crossdomain.xml) must be terminated by a null byte&lt;/li&gt;&lt;li&gt;Policy files served via an xmlsocket must include the to-ports attribute of the allow-access-from tag&lt;/li&gt;&lt;li&gt;Access to ports below 1024 can only be granted via a call to loadPolicyFile&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/div&gt;An example call would be &lt;code&gt;Security.loadPolicyFile("xmlsocket://localhost:5000");&lt;/code&gt; Upon receiving a valid policy file, Flex will then establish the desired socket connection.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Flushing the Flex Socket&lt;/span&gt;&lt;/div&gt;&lt;div&gt;You would think that a call to &lt;code&gt;socket.flush()&lt;/code&gt; would be necessary every time the Flex app writes to the socket.  In practice this isn't the case, but you &lt;span class="Apple-style-span" style="font-style: italic;"&gt;should&lt;/span&gt; always flush anyway.  I only mention this because it is an oddity.  Namely, in Mac OS X  browsers, Flex commands written to the socket are received fine by the server without ever flushing the socket on in the Flex client.  In Windows browsers, however, failing to flush the socket will cause the command not to be sent on all writes to the socket after the first (the first write will go through fine without flushing).  Surprised the hell out of me when I discovered this (which I did by writing the app on a Mac and failing to flush the socket, which worked fine, until I tested it on a Windows machine and found out it wasn't working).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;2 Messages Sent !(necessarily)= 2 Messages Received&lt;/span&gt;&lt;/div&gt;&lt;div&gt;In your protocol decoder (the one that receives incoming requests and converts the bytes into your internal representation of a command), you have to maintain a state to monitor where you are in receiving data.  For example, you may receive one full command, only part of a command, or more than a full command at any given time.  In short, this is due to Nagle's algorithm to increase network efficiency by decreasing the number of packets sent (&lt;a href="http://en.wikipedia.org/wiki/Nagle%27s_algorithm"&gt;full description&lt;/a&gt;).  Your protocol decoder needs to know how to handle these situations.&lt;br /&gt;&lt;br /&gt;The case is simplest when receiving a whole command, since reading all bytes available will result in one full command.  However, the other cases are made easier to handle by including the length of the incoming message in it's header.  For example, I defined a very lightweight protocol of 1 byte to indicate the type of message, 2 bytes to indicate the length of the message, then the message, which could be of variable size.  Thus, when receiving some data, you can check the length to see how much you need, and then handle the incoming data based on how much you have and how much you need.  A longer description with good examples is available &lt;a href="http://mina.apache.org/tutorial-on-protocolcodecfilter.html"&gt;here&lt;/a&gt; on the Mina website.  I mention it here because it is definitely worth reading before writing any code, and if you neglect it, you'll end up with code that will behaive unpredictably.&lt;br /&gt;&lt;br /&gt;That's about all I can think of right now, I started this post days ago and just came back to it today, so I might add anything else if I remember, or maybe as a new post.  Happy socket programming!&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-6326046721719045627?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/6326046721719045627/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=6326046721719045627' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/6326046721719045627'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/6326046721719045627'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/07/socket-programming-with-flex-and-apache.html' title='Socket Programming with Flex and Apache Mina'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-3520112748475727887</id><published>2008-06-30T14:43:00.005-04:00</published><updated>2008-06-30T20:01:36.508-04:00</updated><title type='text'>J2Free 0.1 Alpha Preview</title><content type='html'>A couple months back, I wrote a post titled &lt;a href="http://blog.augmentedfragments.com/2008/04/java-complaints-and-introducing.html"&gt;Java Complaints and Introducing the Redemption of Java: J2Free&lt;/a&gt;, in which I laid out a few of my complains about Java and introduced a new project, called J2Free, that &lt;a href="http://tacoquest.com/"&gt;Arjun&lt;/a&gt; and I had begun.  Well, development of J2Free had stalled for a month or so while I was out of town and working on revamping an older site.  But, much to my great pleasure, we now have the first previews of J2Free to show.&lt;br /&gt;&lt;br /&gt;Today's preview is of the entity administration portion of the project, which uses a heavy amount  of reflection to analyze the structure of persistent entities and expose a CRUD interface into your persistence layer without you ever having to write a line of code.  For now, the only supported persistence provider is Hibernate.  After including the J2Free jar, as well as the JSPs/CSS/JavaScript files, you can begin browsing and editing the entities stored in your persistence layer through a web interface.&lt;br /&gt;&lt;br /&gt;Here's a view of the J2Free Entity Administration section when first loaded.  The entities are listed in tabs across the top.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_PhxljBBvj_U/SGlgRSNy36I/AAAAAAAAABg/eAj0U7WiTNA/s1600-h/1.jpg"&gt;&lt;img style="cursor: pointer;" src="http://bp2.blogger.com/_PhxljBBvj_U/SGlgRSNy36I/AAAAAAAAABg/eAj0U7WiTNA/s400/1.jpg" alt="" id="BLOGGER_PHOTO_ID_5217807493349171106" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;After clicking on a tab, the available entities of that type will be loaded via an AJAX call and populate a list along the left.  Entities are loaded in batches of 100 for now, with an option to load more.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_PhxljBBvj_U/SGlgRsZRx8I/AAAAAAAAABo/mfsmq6m1Mas/s1600-h/2.jpg"&gt;&lt;img style="cursor: pointer;" src="http://bp2.blogger.com/_PhxljBBvj_U/SGlgRsZRx8I/AAAAAAAAABo/mfsmq6m1Mas/s400/2.jpg" alt="" id="BLOGGER_PHOTO_ID_5217807500376655810" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Selecting an entity on the left will open that entity for editing in form that is dynamically generated based on the fields within your entity.  Read-only fields are set as such, and x-to-x mappings to other entities are represented by entity 'boxes'.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_PhxljBBvj_U/SGlgSCNqHrI/AAAAAAAAABw/0MUoCeUE0tU/s1600-h/3.jpg"&gt;&lt;img style="cursor: pointer;" src="http://bp1.blogger.com/_PhxljBBvj_U/SGlgSCNqHrI/AAAAAAAAABw/0MUoCeUE0tU/s400/3.jpg" alt="" id="BLOGGER_PHOTO_ID_5217807506233499314" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Editing a single-entity field, or a collection of entities is done via a third column, the entity browser. From there, entities can be dragged and dropped between a field and the entity browser column.  Click save will save any changes, and clicking delete will delete the entity.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_PhxljBBvj_U/SGlgSYpGBTI/AAAAAAAAAB4/558vc5r2o5c/s1600-h/4.jpg"&gt;&lt;img style="cursor: pointer;" src="http://bp2.blogger.com/_PhxljBBvj_U/SGlgSYpGBTI/AAAAAAAAAB4/558vc5r2o5c/s400/4.jpg" alt="" id="BLOGGER_PHOTO_ID_5217807512254154034" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Any entity can be clicked to display that entities properties in read-only format (to keep confusion about which entity is being edited at a given time to a minimum).&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_PhxljBBvj_U/SGlgS2dCEMI/AAAAAAAAACA/TtvcZFmi5kA/s1600-h/5.jpg"&gt;&lt;img style="cursor: pointer;" src="http://bp0.blogger.com/_PhxljBBvj_U/SGlgS2dCEMI/AAAAAAAAACA/TtvcZFmi5kA/s400/5.jpg" alt="" id="BLOGGER_PHOTO_ID_5217807520256626882" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;At the top of the left-side entity list is a New button that will create a new instance of any type of entity and present you with the same form used for editing entities to populate the fields.  New entities are not persisted until the saved from the edit view.&lt;br /&gt;&lt;br /&gt;There's a lot that needs to be done; including improving the visual display of entity associations, editing one-to-many associations, proper display of embedded IDs, and improvements to handle larger amounts of entity types and larger quantities of entities.  Additionally, there are plans to auto-generate Lucene indexes (with Hibernate search) on String fields to allow searching of all entities.  I also want to include a box for submitting custom queries (in JPQL) that will return the results in the visual entity form.&lt;br /&gt;&lt;br /&gt;Got a long ways to go... but it's far enough along now that it has become functionally useful to me!  The most exciting part is that the reflection necessary to create an entity administration CRUD interface into a persistence structure with no knowledge of the composition is the basis of a lot of what will follow, including access controlled operations that can be exposed as components within a consumer facing site and a fully-functional REST API.  Imagine that, creating an API into your site as easy as setting &lt;code&gt;api.enabled=true&lt;/code&gt; (or for more fine-grain control, setting &lt;code&gt;api.enabled.classes=com.mysite.dao.*&lt;/code&gt;).  Excited yet?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-3520112748475727887?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/3520112748475727887/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=3520112748475727887' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3520112748475727887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3520112748475727887'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/06/j2free-01-alpha-preview.html' title='J2Free 0.1 Alpha Preview'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_PhxljBBvj_U/SGlgRSNy36I/AAAAAAAAABg/eAj0U7WiTNA/s72-c/1.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-768002016688743374</id><published>2008-06-27T03:39:00.002-04:00</published><updated>2008-06-27T03:40:14.504-04:00</updated><title type='text'>LivePipe UI</title><content type='html'>I love Prototype, have since the day I discovered it.  Today, I found something equally awesome.  &lt;a href="http://livepipe.net/"&gt;LivePipe UI&lt;/a&gt;, where have you been my whole life?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-768002016688743374?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/768002016688743374/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=768002016688743374' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/768002016688743374'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/768002016688743374'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/06/livepipe-ui.html' title='LivePipe UI'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-7581180559588377798</id><published>2008-06-19T02:44:00.002-04:00</published><updated>2008-06-19T03:16:34.830-04:00</updated><title type='text'>Adobe Flex + J2EE Security - Mismatched SessionID!</title><content type='html'>So get this.&lt;br /&gt;&lt;br /&gt;I have a servlet called &lt;code&gt;FileUploader&lt;/code&gt;.  It takes a multipart/form-data request and proxies the file to S3.  At the same time, it records some data in our database regarding the user, filename, and so forth.   Simple enough.&lt;br /&gt;&lt;br /&gt;I have a Flex 3 application that acts as a file chooser on the client side.  It propogates a &lt;code&gt;FileReference&lt;/code&gt;, which is then submitted to my &lt;code&gt;FileUploader&lt;/code&gt; servlet via a &lt;code&gt;URLRequest&lt;/code&gt;.  Again, straight forward.&lt;br /&gt;&lt;br /&gt;My Flex application lives in a JSP mapped to &lt;code&gt;/upload&lt;/code&gt;.  I have used declarative security to protect &lt;code&gt;/upload&lt;/code&gt; for all HTTP methods.  I have a servlet that sets a &lt;code&gt;User&lt;/code&gt; object, containing attributes relating to the current user, to the session a after successful login, so that the &lt;code&gt;User&lt;/code&gt; bean will be available to the &lt;code&gt;FileUploader&lt;/code&gt; servlet.&lt;br /&gt;&lt;br /&gt;Here's where it gets interesting...&lt;br /&gt;&lt;br /&gt;I go to &lt;code&gt;/upload&lt;/code&gt; and am redirected to the login page.  I login and am redirected back to &lt;code&gt;/upload&lt;/code&gt;.  The JSP at &lt;code&gt;/upload&lt;/code&gt; outputs some information from the &lt;code&gt;User&lt;/code&gt; bean stored in the session (verifying that the bean is set correctly) as well as the &lt;code&gt;JSESSIONID&lt;/code&gt; and the Flex application.  Great so far. I use the Flex app to choose a file, which is then submitted to the &lt;code&gt;FileUploader&lt;/code&gt; servlet.  The &lt;code&gt;FileUploader&lt;/code&gt; servlet gets the &lt;code&gt;HttpSession&lt;/code&gt; from the request and logs the &lt;code&gt;JSESSIONID&lt;/code&gt;.  Check it the result:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;JSESSIONID&lt;/code&gt; from the JSP: &lt;code&gt;f8e64a4e62840010b6067470a346&lt;/code&gt;&lt;br /&gt;&lt;code&gt;JSESSIONID&lt;/code&gt; from the log:  &lt;code&gt;f8ec7389408a60103998c6bb75f0&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;WHAT!?!?! How did that happen?  Is the Flex application identifying itself to my server as a different user than the browser?  How can this be?  The Flex documentation states that flex applications will use the underlying browser state when interacting with J2EE declarative security.&lt;br /&gt;&lt;br /&gt;WTF Adobe?  Am I surprised that it is not working according to the documentation?  Not in the least.&lt;br /&gt;&lt;br /&gt;Whenever development is going well, and especially when ahead of schedule, some Adobe product will ruin the fun.  Don't believe it?  Google for the Flash External Interface bug where subclass field values will "float" up to their superclass if the superclass and subclass both have a field of the same name... drove me crazy last fall...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-7581180559588377798?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/7581180559588377798/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=7581180559588377798' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/7581180559588377798'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/7581180559588377798'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/06/adobe-flex-j2ee-security-mismatched.html' title='Adobe Flex + J2EE Security - Mismatched SessionID!'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-7573998299090327467</id><published>2008-06-18T18:05:00.002-04:00</published><updated>2008-06-18T18:06:00.744-04:00</updated><title type='text'>Fantasy Congress Redesign</title><content type='html'>Just pushed out a redesigned Fantasy Congress today... &lt;a href="http://www.fantasycongress.com"&gt;check it out&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-7573998299090327467?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/7573998299090327467/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=7573998299090327467' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/7573998299090327467'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/7573998299090327467'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/06/fantasy-congress-redesign.html' title='Fantasy Congress Redesign'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-5889154184802591963</id><published>2008-06-18T02:23:00.003-04:00</published><updated>2008-06-18T02:29:58.204-04:00</updated><title type='text'>Netbeans 6.1 UI Freezes</title><content type='html'>Just the other day I decided to finally try out Netbeans 6 for real.  I've tried out the various betas and release candidates for V6 over the past year or so, but each one has had some major flaw that severely limited by productivity.  I figured that by 6.1 the IDE would be stable enough, but alas it is not. &lt;br /&gt;&lt;br /&gt;Randomly, the UI will freeze up.  It can happen 2 minutes into using the IDE or 6 hours into use.  Sometimes it happens when I click on something, other times it happens without my input at all.  In all cases, the IDE becomes non-responsive to input, but apparently still responsive enough to not show up as non-responsive in Activity Monitor.  I've tried letting the IDE sit for up to 30 minutes to work itself out, but that did not help.  I just end up force-quitting randomly throughout the day.  Extremely frustrating.&lt;br /&gt;&lt;br /&gt;I'm tempted to pull a Windows and revert to Netbeans 5.5, except that I put a great deal of time into customizing Netbeans indentation, syntax highlighting, etc when first setting up 6.1 and I don't want to lose those changes.&lt;br /&gt;&lt;br /&gt;Sure, even Netbeans 5.5 was slow at times, but on the whole I find the UI of 6.1 to be far more sluggish and delayed.  Ah!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-5889154184802591963?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/5889154184802591963/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=5889154184802591963' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5889154184802591963'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5889154184802591963'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/06/netbeans-61-ui-freezes.html' title='Netbeans 6.1 UI Freezes'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-3283020189473376452</id><published>2008-04-23T10:37:00.006-04:00</published><updated>2008-04-23T11:34:43.100-04:00</updated><title type='text'>Prototype DOM Extension Browser Quirks</title><content type='html'>Documentation is really helpful.  Seriously.  In fact, anybody who failed to read the section entitled 'Native Extensions' in the article on &lt;a href="http://www.prototypejs.org/learn/extensions"&gt;How Prototype extends the DOM&lt;/a&gt; would be surprised to find that much of their code would not work in IE while working fine in many browsers.   Until recently, I was one of those people that had not read that article. This is an easy pitfall for developers whose primary development browser is Firefox, or even Opera.&lt;br /&gt;&lt;br /&gt;The problem is that Firefox allows adding methods to prototype of native objects, such as &lt;tt&gt;HTMLElement&lt;/tt&gt;.  Thus, all DOM extensions are are available on all elements, including dynamically created elements.  However, this is not the case with IE. The syntactic shorthand DOM extensions I'm referring to are constructs such as &lt;tt&gt;myDiv.getWidth()&lt;/tt&gt; instead of the longer &lt;tt&gt;Element.getWidth(myDiv)&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;IE does not let anyone touch &lt;tt&gt;HTMLElement.prototype&lt;/tt&gt;, thus DOM extensions are not available by default on elements in IE.  To get around this, Prototype has two constructs to enable DOM extensions on an element:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;tt&gt;Element.extend('my_div')&lt;/tt&gt;, or&lt;/li&gt;&lt;li&gt;&lt;tt&gt;$('my_div')&lt;/tt&gt;&lt;/li&gt;&lt;/ol&gt;Prototype's &lt;tt&gt;$&lt;/tt&gt; function automatically extends any element passed through it, while still being smart enough to not doubly extend any element.  Thus, assuming that you access all elements through &lt;tt&gt;$('elem')&lt;/tt&gt;, you won't have to worry about the Element extensions not being available on a given object.&lt;br /&gt;&lt;br /&gt;The simplest, though more annoying solution, is to not use syntactic extensions and use Element methods directly.  This means always using &lt;tt&gt;Element.doSomething(myElem,...)&lt;/tt&gt; rather than &lt;tt&gt;myElem.doSomething(...)&lt;/tt&gt;.  That's not an ideal solution, and we can do better assuming we can remember to extend elements ourselves, which is as simple as calling &lt;tt&gt;Element.extend(myElem)&lt;/tt&gt;, or wrapping access to the element in a &lt;tt&gt;$&lt;/tt&gt; call, such as &lt;tt&gt;$(myElem)&lt;/tt&gt;.&lt;br /&gt;&lt;br /&gt;There are three sticky situations to watch out for: (1) dynamically created objects, (2) going up or down the DOM chain from an extended element to non-extended elements, and (3) browser objects.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Dynamically Created Elements&lt;/span&gt;&lt;br /&gt;In IE, dynamically created objects are not extended by default, unlike Firefox.  In Firefox, the following will work:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;var myDiv = document.createElement('div');&lt;br /&gt;myDiv.setStyle({width: '50px'});&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Note the DOM extension &lt;tt&gt;.setStyle({})&lt;/tt&gt; is available by default. IE requires an additional step to achieve the same:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;var myDiv = document.createElement('div');&lt;br /&gt;Element.extend(myDiv);&lt;br /&gt;myDiv.setStyle({width: '50px'});&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;It's easy to forget the call to &lt;tt&gt;Element.extend(myDiv)&lt;/tt&gt; when dynamically creating elements, especially because the code will work just fine without that in a few more developer friendly browsers.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Traversing the DOM&lt;/span&gt;&lt;br /&gt;Extending an element does not confer that extension upon elements referenced from said element.  &lt;tt&gt;parentNode&lt;/tt&gt; is a big one to watch out for.  It's really easy to want to do:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;$('myDiv').parentNode.hide();&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;That will work fine in Firefox, but only because all elements are extended by default (as described above).  In IE, you'll have to remember to extend the parent element as well:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;$($('myDiv').parentNode).hide();&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;I agree, it is annoying.  On the good side, since $ is smart enough not to re-extend a previously extended element, we don't have to worry about browsers that extent all elements by default running into problems from the constructs necessary to make IE behave nicely.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Browser Objects&lt;/span&gt;&lt;br /&gt;&lt;tt&gt;document&lt;/tt&gt; is an object, and thus must be extended to enable the syntactic shortcuts.  In Firefox, the following will work fine:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;document.body.insert(myDiv);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;In IE, that will result in the error message, "Object doesn't support this property of method." To make it work in IE, just wrap the call:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;$(document.body).insert(myDiv);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;On the whole, I like the syntactic shortcuts, but as everyone stresses, you have to test the code in multiple browsers.  If my code using Prototype works in Firefox but not IE, these are the first three things I look for.  Happy scripting!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-3283020189473376452?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/3283020189473376452/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=3283020189473376452' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3283020189473376452'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3283020189473376452'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/04/prototype-dom-extension-browser-quirks.html' title='Prototype DOM Extension Browser Quirks'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-6842453711295515347</id><published>2008-04-22T17:09:00.002-04:00</published><updated>2008-04-22T17:49:03.697-04:00</updated><title type='text'>Beer Quest: 22 Down, 478 to go</title><content type='html'>For my birthday last weekend, a friend gave me a copy of &lt;a href="http://www.amazon.com/Michael-Jacksons-Great-Beer-Guide/dp/0789451565"&gt;Michael Jackon's Great Beer Guide to 500 Classic Brews&lt;/a&gt;.  This Michael Jackson is the beer aficionado, not the singer.  And so, I'm going to use the book as a checklist to expand my beer knowledge.  I have had 22 of the 500 beers before receiving the book, and #23 is currently in the fridge.  I am thoroughly looking forward to the next 478 beers!&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here's the current list of beers in the book that I have tried:&lt;br /&gt;&lt;br /&gt;Alaskan Amber&lt;br /&gt;Alaskan Smoked Porter&lt;br /&gt;Bitburger Premium Pils&lt;br /&gt;Boon Geuze&lt;br /&gt;Budweiser Budvar (sold in the US under the name Chechvar)&lt;br /&gt;Fanziskaner Dunkel Hefe-Weissbier&lt;br /&gt;Gorden Biersch Marzen&lt;br /&gt;Gouden Carolus&lt;br /&gt;Guinness Extra Stout&lt;br /&gt;Hoegaarden Witbier&lt;br /&gt;Huyghe Delirium Tremens&lt;br /&gt;Kasteel Bier Donker&lt;br /&gt;Konig Pilsener&lt;br /&gt;Kostitzer Schwarzbier&lt;br /&gt;Newcastle Brown&lt;br /&gt;North Coast Old Rasputin Russian Imperial Stout&lt;br /&gt;Ommegang&lt;br /&gt;Paulaner Hefe-Weizen&lt;br /&gt;Pilsner Urquell&lt;br /&gt;Red Hook ESB&lt;br /&gt;Saison Dupont&lt;br /&gt;Schloss Eggenberg Urbock 23&lt;br /&gt;Singha&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I may not have mentioned it before, but my housemates and I also brew our own beer.  We started last summer and it has been an awesome time.  So far we're brewed an English Brown, an Irish Red, a German Altbier, and an IPA.  Currently underway is a Belgian Triple, and just last night we kegged a Germain Weizenbier.  Next up is a Maibock.  Home-brewing is certainly a lot more work than going to the store and picking up a nice 6-pack, but it's really enjoyable.  Kind of like how steak houses have great food, but sometimes it's way more fun to stand outside in the rain and grill that steak yourself.  So, in addition to tech posts and random thoughts, I think there will be an increased number of posts relating to beer in the future.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-6842453711295515347?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/6842453711295515347/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=6842453711295515347' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/6842453711295515347'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/6842453711295515347'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/04/beer-quest-22-down-478-to-go.html' title='Beer Quest: 22 Down, 478 to go'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-437763096556497070</id><published>2008-04-15T22:45:00.004-04:00</published><updated>2008-04-15T23:32:46.976-04:00</updated><title type='text'>Parallels Desktop Config Recovery</title><content type='html'>Do not run OS X Disk Utility's Erase Free Space feature while Parallels Desktop is running.  I did that today, and my Parallels VM config file disappeared.  Details follow.&lt;br /&gt;&lt;br /&gt;Erasing free space is accomplished in a rather interesting way.  Disk Utility creates one really huge file to fill up the remaining space on your hard drive, then proceeds to write over that space with 0's as many times as you choose (1-pass, 7-pass, or 35-pass).  Thus, at the peak of the process, your system will indicate that your hard drive is full and suggest that you clear space (the free space alert has no awareness of Disk Utilities activities and treats the new large file like any other file).  The technique involved is actually a pretty creative way of letting the operating system unconsciously collect all the free space without having to explicitly write a new routine for collecting free space.  After all, allocating space for a file is just a matter of finding enough free space for said file.&lt;br /&gt;&lt;br /&gt;Anyhow, I had Parallels running while this process was going on and I had forgotten that Parallels requires a certain amount of free space to operate.  When the remaining free space dropped below the required amount (in my case 512MB, though it may vary depending on your VM configuration), the VM alerted me and presented two options: clear enough space and continue operating, or shutdown the VM.  I chose the latter since I wasn't actually using right then.  And that was the final blow...&lt;br /&gt;&lt;br /&gt;When I went to restart Parallels later, I was prompted with the screen to install a new VM.  WTF Parallels?  What happened to the VM I already had installed and have been using for months?  After some investigating, I discovered that the config file for my VM was empty.  That's right, empty.  The file remained, but its contents had disappeared.  Not sure why, but it was disappointing to say the least.&lt;br /&gt;&lt;br /&gt;The fact that I am a Mac user should suggest that my tolerance for Windows XP installs is rather low, so clearly I didn't want to reinstall the VM... if only I had &lt;span style="font-style: italic;"&gt;somebody else's&lt;/span&gt; config file...&lt;br /&gt;&lt;br /&gt;At this point, I had no idea what the structure of a Parallels VM config file was, but I figured that investigating that would be far quicker than an XP install.  Fortunately &lt;a href="http://tacoquest.com/"&gt;Arjun&lt;/a&gt; also uses Parallels, so he sent over his config file, and I broke out the mad doctor goggles and dove in.&lt;br /&gt;&lt;br /&gt;Turns out the VM config file is a simple properties file with [] section headers exactly like a my.cnf MySQL server/client config file.  Furthermore, there are very few properties in there that really matter in terms of the VM running or not, the rest just deal with &lt;span style="font-style: italic;"&gt;how&lt;/span&gt; it runs.  Here they are:&lt;br /&gt;&lt;blockquote&gt;App Version = 3.0.5582&lt;br /&gt;...&lt;br /&gt;Disk 0:0 image = winxp.hdd&lt;/blockquote&gt;The first could be problematic because the config file is going to need to list an App Version compatible with the version of Parallels you have installed.  For example, if you upgraded to Parallels version 3.x.y from version 2.x.y, the installer reformatted your existing VMs to work with version 3.x.y.  If you have version 2.x.y of Parallels, but your config file indicates version 3.x.y, I'd suspect you'd run into some problems...&lt;br /&gt;&lt;br /&gt;The other important property there is just a reference to the name of the Parallels .hdd disk image.  Since Arjun has the same version of Parallels as me, all I had to change was the name of the disk image.&lt;br /&gt;&lt;br /&gt;The only remaining changes were smaller, but I suspect would have caused problems if I hadn't changed them too.  Namely, in the [Shared folders] section, there can be references to shared folders, in which case you'll need to change the location to match your file structure.  I had the following, but yours may be different.&lt;br /&gt;&lt;blockquote&gt;Folder0 path = /Users/Ryan&lt;br /&gt;...&lt;br /&gt;Folder1 path = /Users/Ryan/Documents&lt;br /&gt;&lt;/blockquote&gt;Also, if you remove shared folder mappings, make sure to adjust this property accordingly:&lt;br /&gt;&lt;blockquote&gt;Shared folders count = 2&lt;/blockquote&gt;After making those changes, I fired up Parallels and was back up and running just like before!  Overall, it was a remarkably painless process to recover the VM config file if you happen to have lost it.  At the very least, those few changes will get your VM back up and running, and then you can use the config GUIs to restore your specific preferences.&lt;br /&gt;&lt;br /&gt;As a disclaimer for comments, no, I did not GLOG or RTFM for this, so if it's out there already somewhere, sorry for the duplication.  In all, it was probably faster to try than it would have been to find the answer online anyway.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-437763096556497070?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/437763096556497070/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=437763096556497070' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/437763096556497070'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/437763096556497070'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/04/parallels-desktop-config-recovery.html' title='Parallels Desktop Config Recovery'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-3241128547243786355</id><published>2008-04-02T20:44:00.003-04:00</published><updated>2008-04-02T21:16:15.086-04:00</updated><title type='text'>Java Complaints and Introducing the Redemption of Java: J2Free</title><content type='html'>I'm a big Java fan, so this list is by no means a declaration of war against Java.  There are, however, a few things about the language that I find very annoying:&lt;br /&gt;&lt;br /&gt;1)  Strongly typed.  I enjoy types, I really do.  Sometimes I even love the strongly typed nature because of the kick ass things that can be done with generics and reflection.  Other times, I crave the flexibility of loosely typed languages, such as javascript.  I've found that loose typing can lend itself to more elegant solutions.&lt;br /&gt;&lt;br /&gt;2) No anonymous functions.  If Java treated functions as first-class objects and allowed dynamic creation of functions at runtime, I would be in heaven.  Sometimes, I find scenarios where the most elegant, and quickest to implement, solution would make use of an anonymous function.  Other times I really wish I could pass functions around as arguments.  This would make my life more happy.&lt;br /&gt;&lt;br /&gt;3) == vs. .equals() for equality comparison.  I'm not a big fan of pointers in C and C++.  I prefer array notation in Java, for example.  However, C and C++ pointers make equality comparison very easy and clear.  Namely, it is quite obvious when you are comparing by memory location and when you are comparing by value.  In Java, it is easy for this to escape me momentarily and cause me to spend 5 minutes debugging something so simple as an equality comparison.  For example, comparing int primitive data types with == compares by value.  However, if you happen to be using the int wrapper class, java.lang.Integer, then equality comparisons with == default to comparison by memory location since Integer is an Object.  To compare two Integers by value, you have to use the .equals() method of Integer.  I would much prefer if == in Java defaulted to comparison by value, and .equals() (or something like .objectEquals()) was used for memory location comparison.  I think it would be more straight forward at least when it comes to consistency with the Java primitive data types.&lt;br /&gt;&lt;br /&gt;4) I can't ever find any hosting providers that support Java application servers.  This is a pain because I end up setting up all of our servers, taking me away from tasks I find more interesting, challenging, engaging and, in general, worthwhile.&lt;br /&gt;&lt;br /&gt;In all fairness, I still think the reasons against Java out there are highly exaggerated.  The "experiments" that show Java is less agile, clunkier, and slower to develop all seem to be written by people that don't know Java.  The first time you're trying any framework it's confusing as hell, simple as that.  Java has some basic configuration required for any web-application.  I've been doing it long enough now that I've developed my own framework-ish libraries, which allow me to skip right past all the parts of Java people complain about and jump right into the creation aspect.&lt;br /&gt;&lt;br /&gt;On that note, I've begun development of a consolidation of my tools with the help of one of my Co-Founders at &lt;a href="http://Publi.us/"&gt;Publi.us&lt;/a&gt;, &lt;a href="http://tacoquest.com/"&gt;Arjun&lt;/a&gt;.  We are calling the project J2Free.  It's goal: redemption of Java when compared with frameworks such as Rails &amp;amp; Django.  We started out by thinking up all of the things that annoy us when starting a new Java web-application.  The first step in the J2Free is a smooth process by which users can bypass the annoying crap and jump straight into development.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;J2Free itself will utilize J2EE + JPA (with Hibernate as the persistence provider) + MySQL in a classic MVC architecture.  As a disclaimer, I despise JSF with it's massive xml configuration files and odd String return values from controller classes.  So, don't expect any of that here.  J2Free will feature time saving procedures such as auto-generation of an admin section and fun little tidbits such as taglibs for pagination of datasets, templating, caching, and display formatting (Date formatting, quote escaping, etc).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Additionally, J2Free will make it easy to automatically open controller (or subsets of controllers, such as read-only functions) classes via API.  Want an API? "Click. Done."  Since I am a big fan of DWR, I plan on including that same "Click. Done." ease for enabling AJAX via DWR.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Clearly, this is all very early, but we're getting started with planning so now is the best time to throw out ideas!  When complete, we plan on making J2Free available freely and we will be open sourcing the project at some point as well.  Anyway, something to look forward to!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-3241128547243786355?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/3241128547243786355/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=3241128547243786355' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3241128547243786355'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3241128547243786355'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/04/java-complaints-and-introducing.html' title='Java Complaints and Introducing the Redemption of Java: J2Free'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-3753347556082716084</id><published>2008-04-01T14:00:00.006-04:00</published><updated>2008-04-01T14:11:32.939-04:00</updated><title type='text'>Interesting 30-second Snippet for Emailing Websites to Friends</title><content type='html'>So, one of my co-founders was telling me today about a startup out there that basically is del.icio.us but designed for sending articles to your friends.  I realized that I could implement 95% of the "sending articles to your friends" functionality in 30 seconds.  The code:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;a href="javascript:location.href='mailto:?subject=Check Out: \'' + document.title + '\'&amp;amp;body=Thought you might like this: ' + window.location.href;"&amp;gt;Send to Friends&amp;lt;/a&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;It's just a Del.icio.us-style bookmarklet that opens your mail client with the current page title in the subject and the link to the current page in the body.  Simple, clean, done.  Now I can just click that when I want to email a page I'm on to friends.&lt;br /&gt;&lt;br /&gt;For simplicity, you can drag this link to your bookmarks bar to add the feature yourself.&lt;br /&gt;&lt;br /&gt;&lt;a href="javascript:location.href='mailto:?subject=Check Out: \'' + document.title + '\'&amp;amp;body=Thought you might like this: ' + window.location.href;"&gt;E-mail to Friends&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-3753347556082716084?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/3753347556082716084/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=3753347556082716084' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3753347556082716084'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3753347556082716084'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/04/interesting-30-second-snippet-for.html' title='Interesting 30-second Snippet for Emailing Websites to Friends'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-723579494012581110</id><published>2008-02-27T13:40:00.002-05:00</published><updated>2008-02-27T13:44:47.968-05:00</updated><title type='text'>PHOTOREE Image Recommendation System</title><content type='html'>For your mindless clicking enjoyment...&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.photoree.com"&gt;http://www.photoree.com&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is a fun little site I discovered the other day.  You give the thumbs up or down to photos and it recommends other photos that it thinks you might like.  Sounds like straight forward user-based collaborative filtering, though maybe it's more technically advanced and has some sort of image analysis system and is actually using item-based collaborative filtering...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-723579494012581110?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/723579494012581110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=723579494012581110' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/723579494012581110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/723579494012581110'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/02/photoree-image-recommendation-system.html' title='PHOTOREE Image Recommendation System'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-4842410273138229292</id><published>2008-02-27T12:28:00.006-05:00</published><updated>2008-02-27T13:36:29.930-05:00</updated><title type='text'>MacBook Pro Multi-touch Trackpad</title><content type='html'>I have an ongoing debate with &lt;a href="http://www.arjunlall.com/"&gt;Arjun&lt;/a&gt; over the new MacBook Pro with the Multi-Touch trackpad (the kind first introduced on the MacBook Air).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Question&lt;/span&gt;&lt;br /&gt;Are the additional multi-touch input gestures in the new MBP a result of a software/firmware or a hardware upgrade?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;My Position&lt;/span&gt;&lt;br /&gt;The hardware of the trackpad must be different in the new MBP, otherwise Apple would have released the upgrade as a firmware/software update, benefiting a larger audience, rather than as a new feature on a new machine.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Arjun's Position&lt;/span&gt;&lt;br /&gt;The hardware is the same, and Apple chose not to release the new feature as a firmware/software update so as to exaggerate the "new" features of the new MBP and further distinguish the new model from the current model.&lt;br /&gt;&lt;br /&gt;Admittedly, my view holds Apple's integrity in high-esteem.  If I'm wrong, that esteem will no longer be so high.  I hope that Arjun is right, if only because that would mean Apple could (would?) eventually release the same firmware/software for the last-gen MBP, or some 3rd party could attempt to do so.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Evidence&lt;/span&gt;&lt;br /&gt;First, from pictures, the trackpad on the new and last-gen models are visually the same, as opposed to the MacBook Air trackpad which is visually larger.  Although not conclusive either way, it suggests a similarity in hardware.&lt;br /&gt;&lt;br /&gt;Second, the last-gen model trackpad can sense multiple sources of input; two-finger tapping is distinguished from one-finger tapping for right and left click respectively, and two-finger scrolling is recognized for page scrolling.&lt;br /&gt;&lt;br /&gt;As further evidence (and the most convincing in terms of Arjun's argument), the multi-touch capability of the last-gen models is independent of finger position, meaning that a two-finger tap with fingers side by side is recognized just as well as a two-finger tap with fingers spread to opposite sides of the trackpad.  This position indifference holds true for two-finger scrolling; dragging two spread fingers works exactly the same as dragging two touching side by side fingers.&lt;br /&gt;&lt;br /&gt;The logical inference from the above is that if two fingers spread wide can be dragged in a straight line for scrolling, then those same two fingers spread wide should be able to be rotated in a circular motion for, say, rotating a photo in iPhoto.  Because of the hardware necessary to detect two, spread fingers moving simultaneously is already in place in the last-gen model, it isn't too big a jump to suspect the additional gestures added in the new MBP are a result of upgraded firmware/software.&lt;br /&gt;&lt;br /&gt;However, one caveat to note is the nature of multi-touch input in the last-gen modes.  Namely, all supported multi-touch gestures involve parallel motion; (1) two-finger tapping is recognized only when both fingers tap simultaneously, as opposed to having one finger in contact with the trackpad and then tapping with the other, and (2) two-finger scrolling involves both contacts with the hardware moving in the same direction.&lt;br /&gt;&lt;br /&gt;The exception to my parallel inference in the last-gen models is that two fingered scrolling does not require both fingers to be moving, but works just fine if one finger is in contact with the trackpad and the other is moving.&lt;br /&gt;&lt;br /&gt;The gestures introduced in the new MBP break the need for parallel motion; 'pinching' is convergent motion, 'spreading' is divergent, 'rotating' is inverse motion, etc.  The exception is three fingered 'swiping', though the ability to sense more than 2 sources of simultaneous input may be a hardware improvement in the new MBP.&lt;br /&gt;&lt;br /&gt;At this point, it's all speculative, though I hope that somebody with more knowledge in this subject will chime in resolve the debate.  Otherwise, maybe we'll hear from Apple some day.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-4842410273138229292?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/4842410273138229292/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=4842410273138229292' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/4842410273138229292'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/4842410273138229292'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/02/macbook-pro-multi-touch-trackpad.html' title='MacBook Pro Multi-touch Trackpad'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-8589336837488859363</id><published>2008-02-22T14:40:00.011-05:00</published><updated>2008-02-27T12:28:40.879-05:00</updated><title type='text'>Why I Don't Use My Del.icio.us Account</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Introducing the Need&lt;/span&gt;&lt;br /&gt;I have a del.icio.us account.  I've used it sparingly from time to time and I actually think the concept is very good.  Depending on my mood and task at hand, I'll browse the web with Firefox, Camino, or Safari.  I wouldn't use Firefox except for the Firebug   plugin, so that's mainly for developing.  The problem is that there's no predicting = what browser I'm going to be using when I stumble across a website that I want to bookmark.  On the flip side, there are certain sites I want to have bookmarked in all my browsers.  As a result, the browsers share certain bookmarks but each have their own unique finds and so when recalling a site I want to revisit, I can never remember which browser I was using at the time, and thus have to check all three to find the bookmark again.&lt;br /&gt;&lt;br /&gt;That is why I wish del.icio.us did not suck so much.  If not for the forthcoming problems, it would solve my bookmark woes.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Now to the Problems&lt;/span&gt;&lt;br /&gt;First, I dislike the homepage. It assaults my eyes with so much web-safe blue that it all blends together and I feel like I'm looking at a lava-lamp with white liquid and blue lava.  The addition of pictures smoothed it out a bit, but it's still a painful experience.&lt;br /&gt;&lt;br /&gt;On it's own, that would not be enough to keep me away, which brings me to my second complaint.&lt;br /&gt;&lt;br /&gt;Exhibit A:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_PhxljBBvj_U/R78py1k-_NI/AAAAAAAAABY/AXXxfey06hM/s1600-h/Picture+4.png"&gt;&lt;img style="cursor: pointer;" src="http://bp2.blogger.com/_PhxljBBvj_U/R78py1k-_NI/AAAAAAAAABY/AXXxfey06hM/s400/Picture+4.png" alt="" id="BLOGGER_PHOTO_ID_5169896850597018834" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;#9999FF is not a good font-color on top of various shades of pink.  Is it too much to ask for a little contrast?  I agree, it is a very cool effect to calculate the color of some element based on collected data (I've even done it for a project). Nevertheless, there is always one overriding concern: &lt;span style="font-style: italic;"&gt;Make sure that text in the variable color element is still readable.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Even so, the contrast issues wouldn't keep me away, especially since I don't particularly care how many other people have saved my bookmark.  My third complaint is the real reason I don't use my del.icio.us account.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Fatal Flaw&lt;/span&gt;&lt;br /&gt;The bookmarklet is useless for what I want to do with it.&lt;br /&gt;&lt;br /&gt; &lt;/div&gt;&lt;div&gt; &lt;/div&gt;&lt;div&gt;Sure, I can just drop it in my bookmarks bar and click it when I'm on a page I want to add.  But what does this do?  It takes me to del.icio.us where I have to sign into my account to save the bookmark.  This is a hassle.  When I use the bookmarklet, it's because I &lt;span class="Apple-style-span" style="font-style: italic;"&gt;don't want&lt;/span&gt; to go to del.icio.us!&lt;br /&gt;&lt;br /&gt;I never visit del.icio.us, so I'm never signed in. Furthermore, I don't want to have to sign in just to save a bookmark. I don't want to &lt;span style="font-style: italic;"&gt;use&lt;/span&gt; del.icio.us right then and there. I just want to throw something at them real quick, and then come back to it later.&lt;br /&gt;&lt;br /&gt;One of the problems is that browser security constraints disallow sending an asynchronous HTTP request by clicking on a bookmarklet.  This would be the ideal solution, since it would not even require opening a new window or moving away from the current page.  However, as it is not plausible, I'll focus on the sign-in issue.&lt;br /&gt;&lt;br /&gt;The fact of the matter is there exists a simple, safe, secure alternative.  All the bookmarklet does is redirect the browser to del.icio.us's page for submitting links and passes along information about where you just were (specifically, the url and the window title).  The base dilemma is that the bookmarklet is a generic script-- that is, each user has the exact same code in their bookmarklet.  The solution is to make the bookmarklet specific to each user. &lt;br /&gt;&lt;br /&gt;Del.icio.us could tack on two more parameters to the query string above that would contain (1) either your user ID or username (both public information, so no security issues yet), and (2) a hash that would be (a) unpredictable, and (b) unique to each user.  Basically, it's like having a secret piece of information that only you and del.icio.us know and that is only valid for one user and for one-way communication to del.icio.us. For those familiar with public/private key security, this should sound similar.  That way, when clicking the bookmarklet, del.icio.us already knows who you are and doesn't have to ask you to sign in first.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Potential Problems&lt;/span&gt;&lt;br /&gt;Alright, so what about multi-user environments?  First, del.icio.us bookmarklets are probably not going to be found on massively public computers such as libraries and schools.  To accommodate the rare case, del.icio.us could keep the generic version available.  Second, in the home, different user accounts should accompany different users, thus the operating system manages user settings.  Third, what if somebody is on my computer and clicks my "post to del.icio.us" bookmarklet?  Just add a "Not userA?" link on the post page that allows a user to login to a different account, problem solved. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Short Version&lt;/span&gt;&lt;br /&gt;I like the idea of del.icio.us, but it's just as much of a hassle as synchronizing my local browser bookmarks until there is a solution that does not require me to go to del.icio.us to submit a bookmark.&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/8840085855436806841-8589336837488859363?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/8589336837488859363/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=8589336837488859363' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/8589336837488859363'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/8589336837488859363'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/02/why-i-dont-use-my-delicious-account.html' title='Why I Don&apos;t Use My Del.icio.us Account'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_PhxljBBvj_U/R78py1k-_NI/AAAAAAAAABY/AXXxfey06hM/s72-c/Picture+4.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-6242067981214787399</id><published>2008-02-13T12:48:00.003-05:00</published><updated>2009-04-06T04:37:17.610-04:00</updated><title type='text'>Amazon.com's Design Usability Problems</title><content type='html'>I have issues with Amazon.com.  Namely, it always takes me ridiculously longer than necessary to accomplish anything on the site.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Sign In Link&lt;/span&gt;&lt;br /&gt;At the top of the page is a line reading "Hello. Sign in to get personalized recommendations."  For some reason, the part of that line that links to the login page is "personalized recommendations" rather than "Sign in" or even the whole sentence.  My reasons for being on the site had nothing to do with personalized recommendations.  Ergo, I stayed away from  the mysterious "personalized recommendations" link and was completely frustrated by the time I finally clicked it out of desperation and accidentally stumbled upon the login page.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Gift Card Purchases&lt;/span&gt;&lt;br /&gt;I wanted to use a gift card that I had applied to my account to buy a song.  For the life of me , I could not find the option to use the gift card for the purchase.  I was signed in.  I found the song.  I clicked purchase.  There was no option to select a gift card from my account as the payment method.  There was an option to apply a new gift card, but none to use a previously applied gift card.  Eventually I said, "f*** this, it's only $1, I'll just use my credit card."&lt;br /&gt;&lt;br /&gt;My credit card, however, was never charged.  In investigating why, I discovered that gift cards on your account are automatically applied to purchases before whatever payment form you provide.&lt;br /&gt;&lt;br /&gt;That leads to a few thoughts:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Why not let me choose to use a gift card, or choose not to?  Seems odd to force me to use a gift card just because I applied it to my account for safe-keeping.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;If they're going to force me to use the gift card first, why not check to see if I have one &lt;span style="font-style: italic;"&gt;before&lt;/span&gt; making me input my credit card information, thus saving me the hassle of filling out an unnecessary form.&lt;/li&gt;&lt;/ol&gt;Well, that's the shortlist of my recent complains regarding Amazon.com's usability...&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;Update&lt;/span&gt;&lt;/div&gt;&lt;div&gt;I was recently told this was, "because you're not dumb enough to appreciate Amazon's idiotic structure."  I'll take that as a compliment.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-6242067981214787399?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/6242067981214787399/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=6242067981214787399' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/6242067981214787399'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/6242067981214787399'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/02/amazoncoms-design-usability-problems.html' title='Amazon.com&apos;s Design Usability Problems'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-5708466730536492354</id><published>2008-02-13T11:30:00.003-05:00</published><updated>2008-02-13T12:47:59.899-05:00</updated><title type='text'>Why AugmentedFragments.com for the domain?</title><content type='html'>I thought that the reasons behind the domain name deserved a post.  I meant to do this a while ago to christen the blog, but just didn't get around to it, so here we go.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;First the Why&lt;/span&gt;&lt;br /&gt;A few trivial reasons:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;My name is common enough that every combination I could justify owning as a domain name was taken long before the idea occurred to me&lt;/li&gt;&lt;li&gt;I wanted something unique, reflective of me, not reflective of the times&lt;/li&gt;&lt;li&gt;I wanted a .com domain&lt;br /&gt;&lt;/li&gt;&lt;li&gt;AugmentedFragments.com was available (likely because of it's obscurity, length, and likelihood of misspelling)&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Now the How&lt;/span&gt;&lt;br /&gt;At the most basic level, this blog is a partial record of my thoughts.  Fragments, you could say, of my thought process.  To take it a step further, I don't always stop to explain my thoughts to myself, but in this blog I do attempt to clarify and analyze.  Ergo, the fragments are modified, expounded upon, hopefully improved. &lt;br /&gt;&lt;br /&gt;From "improved" I inferred progress in the upward direction and then my background as a musician took over.  Up translated as a higher pitch.  Fragments became chords, as a piece of music may be digested down to the building blocks underlying the complexity of the melody, rhythm, intonation, and color.&lt;br /&gt;&lt;br /&gt;So what happens if you take a chord and "move it up"?  If you maintain the same chord, then "moving up" would probably just be playing the first or second inversion.  That definition doesn't fit well, since essentially it means reordering the notes within the chord.  All the same notes are there, nothing has changed but the order.&lt;br /&gt;&lt;br /&gt;I view my posts as more than a reordering of my thoughts, so I needed something that modified the root chord, and yet still "moved it up."  And thus I arrived on augmented chords.  An augmented triad is a chord with a raised fifth (basically, the high note of the chord is a little higher than usual) resulting in two symmetrical intervals, between the first and third, and the third and fifth, that split the octave evenly.&lt;br /&gt;&lt;br /&gt;Augmented chords are dissonant, unresolved, but that's not the implication I am trying to convey.  An additional feature of an augmented chord is that, beginning with a major augmented triad in an arbitrary (I'll explain why arbitrary shortly) root position, the first inversion is a root position augmented triad beginning on the third and the second inversion is a root position augmented triad beginning on the fifth.  Thus, the root of an augmented chord is ambiguous since it could be any one of the three notes forming the triad.  This may seem problematic, but is actually quite wonderful if you happen to be an accompanying musician since it increases the available chord shapes which will "sound good."&lt;br /&gt;&lt;br /&gt;Thus, augmented fragments are supposed to be portions of my thought process which have been both clarified and obscured so that they are easily understood and readily applicable to a variety of situations.&lt;br /&gt;&lt;br /&gt;I'm not going to give you a fish here, but hopefully you'll come away with a few more hints on how to fish.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-5708466730536492354?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/5708466730536492354/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=5708466730536492354' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5708466730536492354'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5708466730536492354'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/02/why-augmentedfragmentscom-for-domain.html' title='Why AugmentedFragments.com for the domain?'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-3625662876961234723</id><published>2008-01-18T09:48:00.000-05:00</published><updated>2008-01-19T13:30:54.338-05:00</updated><title type='text'>Compressing and Obfuscating JavaScript (with Prototype &amp; Script.aculo.us)</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Abstract&lt;/span&gt;&lt;br /&gt;My methods, trials and eventual solution for optimizing the delivery of JavaScript files via minification, obfuscation, and compression, including examples with notoriously problematic to minify libraries such as Prototype and Script.aculo.us.  Some of my explanation will work on any platform, although my eventual sweet solution is Java specific and relies on a JSP taglib.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Introduction&lt;/span&gt;&lt;br /&gt;Last fall, &lt;a href="http://publi.us/"&gt;my company&lt;/a&gt; developed an election game for the primaries and caucuses called &lt;a href="http://kingmaker.politico.com/"&gt;Kingmaker&lt;/a&gt;, which we launched in the middle of December.  We built portions of the site in Adobe Flex.  Although this provided some cool functionality, it increased our page sizes significantly.  So, I began my search for ways to shrink down everything else on the site to compensate for the tremendous increase caused by Flex apps.&lt;br /&gt;&lt;br /&gt;First, we compressed the images as far as possible before losing too much quality.  Next, I saw to the shrinking of our JavaScript files.  This presented a series of problems:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;I like Prototype.  I wanted to keep it.  But it's huge (124K uncompressed).&lt;/li&gt;&lt;li&gt;I like Script.aculo.us.  I wanted to keep that too.  Again, it's huge (I only use effects, controls, slider, and dragdrop... 111K uncompressed)&lt;/li&gt;&lt;li&gt;My list of known, and highly used JavaScript compressors were known not to be able to compress Prototype or Script.aculo.us correctly.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;"&gt;Why Minify/Obfuscate/Compress Anyway?&lt;/span&gt;&lt;br /&gt;At the most basic level, regardless of connection speeds, smaller files translate into shorter download times.  Sure, web browsers cache static content such as stylesheets and JavaScript files, but there is no justification for  unnecessarily slowing the initial download, or relying on the client when you can do something about it quickly and easily.&lt;br /&gt;&lt;br /&gt;As for obfuscation, you can't ever really protect JavaScript like you can server-side code/bytecode, and you shouldn't be writing JavaScript that make it easy for a malicious user to manipulate your system.  Bottom line: a security risk is still a security risk when obfuscated and you should avoid it all together.  That said, with a system as easy to use as the one I'll outline shortly, there's no reason not make a malicious user's goals just a little bit more difficult.  On another level, perhaps there are some people you want to share your code with, and some people you don't. Obfuscation gives you more control, since obfuscated code is a major pain to read, understand, and modify safely.  Plus, the substitution of tiny meaningless variable names also means a smaller size file (minimally, but smaller nonetheless).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;What Didn't Work&lt;/span&gt;&lt;br /&gt;&lt;a href="http://dean.edwards.name/packer/"&gt;Dean Edwards packer&lt;/a&gt; is a pretty common tool for packing JavaScript files.  There's an online (JavaScript) version, as well as offline versions in .NET, perl, and PHP. Packer, however, uses regex, has strict syntax requirements for it to work that Prototype is known to break.  Also, even after alleviating Prototype's syntax issues, &lt;a href="http://andrewdupont.net/2007/02/26/packing-prototype/"&gt;the online version of packer still chokes on a few properties&lt;/a&gt;, though the offline versions do not.  It's reported that there is an online version of the PHP version that does work, located &lt;a href="http://joliclic.free.fr/php/javascript-packer/en/index.php"&gt;here&lt;/a&gt;, but it didn't work when I was trying it.  Besides, you still have to fix up the syntax first, and even then there's multiple better solutions.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://javascript.crockford.com/jsmin.html"&gt;JSMin&lt;/a&gt; is also regex based, so it has the same issues.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Fixing Prototype's Syntax&lt;/span&gt;&lt;br /&gt;I really shouldn't say fixing... because technically (as in, according to JavaScript interpreters) what Prototype does is 100% OK.  Nevertheless, regex based parsers need stricter syntax than a JavaScript interpreter.  Fortunately, the guys over at &lt;a href="http://dojotoolkit.org/"&gt;Dojo&lt;/a&gt; had the wonderful insight to ditch the regex all together and use (you guessed it) a JavaScript interpreter! Specifically, they use a custom version of &lt;a href="http://www.mozilla.org/rhino/"&gt;Rhino&lt;/a&gt;, which is an open source JavaScript interpreter used in Mozilla applications (Firefox, Camino, Thunderbird, Seamonkey, etc).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.mozilla.org/rhino/"&gt;Rhino&lt;/a&gt; is written in Java, and as a JavaScript interpreter has more context about what's going on in a JavaScript file than a rigid regex does.  Hence, &lt;a href="http://shrinksafe.dojotoolkit.org/"&gt;Dojo ShrinkSafe&lt;/a&gt; came into the world with the ability to safely compress JavaScript files without additional strict syntax requirements.  By running files through Dojo ShrinkSafe first, they could then be packed/obfuscated additionally by regex packers because the post Rhino treatment was syntactically 'fixed' for a regex packer.&lt;br /&gt;&lt;br /&gt;But there was a remaining problem:  If you're using the Dojo Toolkit, then your files are automatically compressed using ShrinkSafe, but I am not and I didn't want to have to go through those steps to manually re-minify a JavaScript file every time I edited it... so my search continued.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;A Changing Solution&lt;/span&gt;&lt;br /&gt;Shortly, I discovered the &lt;a href="http://www.galan.de/projects/packtag"&gt;pack:tag JSP Tag Library&lt;/a&gt;, which seemed to be what I was looking for.  The pack:tag had many benefits:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Simultaneous minification and obfuscation&lt;br /&gt;&lt;/li&gt;&lt;li&gt;gzip compression&lt;/li&gt;&lt;li&gt;Combination of static resources to minimize round-trips from client to server&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Caching of compressed static resources via a memory (Servlet) or file cache, and thus minification, obfuscation, and compression at resource request time (as opposed to compile time)&lt;/li&gt;&lt;li&gt;Pluggable compression algorithms (and an implementable interface for creating your own packing strategy)&lt;/li&gt;&lt;li&gt;Configuration via a .properties file&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;Four packing strategies are included: 2 for CSS (&lt;a href="http://www.julienlecomte.net/blog/2007/08/27/yui-compressor-version-20-now-available/"&gt;Isaac Shlueter's CSS Compressor&lt;/a&gt;, and &lt;a href="http://www.ibloomstudios.com/articles/php_css_compressor/"&gt;the iBloom CSS Compressor&lt;/a&gt;), and 2 for JavaScript(&lt;a href="http://www.crockford.com/javascript/jsmin.html"&gt;JSMin&lt;/a&gt;, and the &lt;a href="http://www.julienlecomte.net/yuicompressor"&gt;YUI Compressor&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;My favorite additional benefits are as follows:&lt;br /&gt;&lt;br /&gt;First, the pack:tag allows you to edit your JavaScript files in their un-minified and un-obfuscated form, thus relinquishing me from having to either edit a minified file or use Dojo Shrinksafe and the perl version of Dean Edward's packer every time I changed a file.&lt;br /&gt;&lt;br /&gt;Second, the pack:tag checks to ensure that a resource is not included more than once in the same page, automatically ignoring subsequent requests, which can accidentally happen quite easily when using multiple JSP includes (used in multiple places) to dynamically build a page.&lt;br /&gt;&lt;br /&gt;Third, the pack:tag allows you to keep your uncompressed (and thus easy to read and edit) JavaScript files within the WEB-INF directory of a web application, where they are protected from prying eyes.&lt;br /&gt;&lt;br /&gt;Using the pack:tag as simple as including the JSP Taglib declaration in your JSP, and then using the following instead of normal &amp;lt;script&amp;gt; tags to include JavaScript files:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;pack:script src="/my/file.js" /&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Or, to combine multiple resources:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;pack:script&amp;gt;&lt;br /&gt;&amp;lt;src&amp;gt;/my/file1.js&amp;lt;/&amp;gt;&lt;br /&gt;&amp;lt;src&amp;gt;/my/file2.js&amp;lt;/&amp;gt;&lt;br /&gt;&amp;lt;/pack:script&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;pack:tag Refined&lt;/span&gt;&lt;br /&gt;The only remaining problem with the pack:tag was the same one I discussed earlier: JSMin and other regex based packers do not safely minify Prototype.  Fortunately, pluggable compressor strategies come to the rescue!  As of version 2.2, the YUICompressor can safely compress Prototype (thanks to the fact that it, like Dojo ShrinkSafe, is implemented using Rhino).  This was an amazing development because I now had a solution whereby I could hide my JavaScript files, edit them with complete clarity, and have them automatically combined, minified, obfuscated, and gzip compressed at request time, then cached for the next request!&lt;br /&gt;&lt;br /&gt;An added benefit of using the YUICompressor is a significant amount of logging output that complains to you're doing something stupid.  Unfortunately, I haven't found a way to turn off that output, so it does fill up the log a bit when compressing files wherein I reference functions that are declared in other files (which it has no context of, unless the files are combined before processing).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Statistical Results&lt;/span&gt;&lt;br /&gt;For testing, all downloaded file size measurements were taken using the Firefox plugin &lt;a href="http://www.getfirebug.com/"&gt;Firebug&lt;/a&gt;, version 1.05.&lt;br /&gt;&lt;br /&gt;I wanted large, commonly used libraries for testing.  Since I happen to be a big fan of &lt;a href="http://www.prototypejs.org/"&gt;Prototype&lt;/a&gt; and &lt;a href="http://script.aculo.us/"&gt;Script.aculo.us&lt;/a&gt;, and since Prototype has been my resident problem in this description, I decided those two libraries would be sufficient.  I use the same technique outlined above on my own JavaScript files with wonderful results.  Since I mostly use Script.aculo.us for the effects, dragdrop, and slider, and thus never include all the components at once, I decided to measure each file individually rather than Script.aculo.us as a whole package.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Terminology&lt;/span&gt;&lt;br /&gt;Minification - refers to both minifying and obfuscating the JavaScript&lt;br /&gt;Individual - Each JavaScript file was included separately in the page; for the base case via :&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;script type="text/javascript" src="/js/prototype.js"&amp;gt;&amp;lt;script&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Or for the other scenarios using pack:tag to include each with:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;pack:script src="/js/prototype.js" /&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Grouped - The JavaScrpt files were included together in one file.  For the case with minification and gzip via:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;pack:tag&amp;gt;&lt;br /&gt;&amp;lt;src&amp;gt;/js/prototype.js&amp;lt;/src&amp;gt;&lt;br /&gt;&amp;lt;src&amp;gt;/js/effects.js&amp;lt;/src&amp;gt;&lt;br /&gt;&amp;lt;src&amp;gt;/js/controls.js&amp;lt;/src&amp;gt;&lt;br /&gt;&amp;lt;src&amp;gt;/js/slider.js&amp;lt;/src&amp;gt;&lt;br /&gt;&amp;lt;src&amp;gt;/js/builder.js&amp;lt;/src&amp;gt;&lt;br /&gt;&amp;lt;src&amp;gt;/js/dragdrop.js&amp;lt;/src&amp;gt;&lt;br /&gt;&amp;lt;src&amp;gt;/js/sound.js&amp;lt;/src&amp;gt;&lt;br /&gt;&amp;lt;/pack:tag&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;For the minified test without gzip, I created a new file (total.js) with the output of the above grouping and included it via:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;script type="text/javascript" src="/js/total.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Versions used in testing&lt;/span&gt;&lt;br /&gt;Prototype 1.6&lt;br /&gt;Script.aculo.us 1.7.1 Beta 3&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Individual Results&lt;/span&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_PhxljBBvj_U/R5JBp1qvPnI/AAAAAAAAAA8/k6IcpWh8RTw/s1600-h/Picture+1.png"&gt;&lt;img style="cursor: pointer;" src="http://bp2.blogger.com/_PhxljBBvj_U/R5JBp1qvPnI/AAAAAAAAAA8/k6IcpWh8RTw/s400/Picture+1.png" alt="" id="BLOGGER_PHOTO_ID_5157256710329417330" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Grouped Results&lt;/span&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_PhxljBBvj_U/R5JBqFqvPoI/AAAAAAAAABE/14lXN_V8q6M/s1600-h/Picture+2.png"&gt;&lt;img style="cursor: pointer;" src="http://bp3.blogger.com/_PhxljBBvj_U/R5JBqFqvPoI/AAAAAAAAABE/14lXN_V8q6M/s400/Picture+2.png" alt="" id="BLOGGER_PHOTO_ID_5157256714624384642" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Conclusion&lt;/span&gt;&lt;br /&gt;Compression in some form can significantly decrease file size and, implicitly, download times.  Different methods of compression yield varying degrees of benefit.   Namely, Gzip (which, by the way, is Prototype's 'officially supported' compression method) yields the greatest initial benefit, with minification yielding additional benefit.  On the whole, I now have a setup that I don't have to do anything.  I create my JavaScript files as if I were not compressing, include them with slightly different syntax, and the pack:tag with the YUICompressor strategy takes care of everything else.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Adendum&lt;/span&gt;&lt;br /&gt;As a testament to this conclusion, more compression options have become available lately.  John-David Dalton has released &lt;a href="http://groups.google.com/group/prototype-core"&gt;a collection of compressed Prototype and Script.aculo.us versions&lt;/a&gt; on Google Code.  He explained his process in an Axajian post last December:&lt;br /&gt;&lt;blockquote style="font-size: 9pt;"&gt;&lt;p&gt;I format the code manually, fixing semi-colons and fixing references to $super. I run them through a compressor with quotes around the $super vars so they aren’t changed then fix their method arguments. I use Dean Edward’s Packer because it creates the smallest files. From there you can use a server side solution to gzip/version/and deploy the file. I use Prado (www.pradosoft.com) and their asset publishing capabilities.&lt;/p&gt;&lt;p&gt;I have a Blog but it’s currently in the early stages, I never have time to work on it.&lt;/p&gt;&lt;/blockquote&gt;Basically it's the process I described above, except manually fixing the syntax 'problems' rather than using a JavaScript interpreter to do it for you.  It's a great solution for non-Java server environments where the pack:tag is not an option.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-3625662876961234723?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/3625662876961234723/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=3625662876961234723' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3625662876961234723'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3625662876961234723'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/01/compressing-and-obfuscating-javascript.html' title='Compressing and Obfuscating JavaScript (with Prototype &amp; Script.aculo.us)'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_PhxljBBvj_U/R5JBp1qvPnI/AAAAAAAAAA8/k6IcpWh8RTw/s72-c/Picture+1.png' height='72' width='72'/><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-3346724907544541897</id><published>2008-01-10T10:11:00.000-05:00</published><updated>2008-01-10T10:35:31.277-05:00</updated><title type='text'>Update and things to come</title><content type='html'>Been a while since I've posted.  To update, I was swamped working on my latest project, &lt;a href="http://kingmaker.politico.com"&gt;Kingmaker&lt;/a&gt;, but there should be a flurry of blog posts soon as I have a backlog of ideas I want to write about.&lt;br /&gt;&lt;br /&gt;In other good news, I was introduced to an awesome program for OS X called &lt;a href="http://journler.com/"&gt;Journler&lt;/a&gt;, which has become one of my most frequently used programs due to its versatility and thus my ability to use it practically however I want.  It is now my second most recommended download for OS X following &lt;a href="http://docs.blacktree.com/quicksilver/quicksilver"&gt;Quicksilver&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;I finally upgraded my Apple MacBook Pro to 3GB of RAM.  I have had 2GB since I first got it.  Although that was fine for a while, I discovered that attempting to run Netbeans, Adobe Flex Builder, and Photoshop with the usual accompaniment of Apple Mail, web browsers, iChat, and iTunes resulted in a ridiculously high degree of paging and significantly decreased my productivity as I sat there waiting to switch between programs or waiting for programs to start and exit. &lt;br /&gt;&lt;br /&gt;Adding the additional RAM made an incredible difference and my productivity has been much the better for it.  I only wish that I could have 4GB... which brings to mind that I debated putting in a matched pair of 2GB sticks knowing that my MBP actually supports up to 3.3GB.  Ultimately, I decided the extra 0.3GB and having a matched pair was not worth the price of the stick.  I had debated moving to 3GB knowing that doing so would mean not having a matched pair and thus losing the 6-8% performance increase recorded for using a matched pair in MBPs.  I reasoned that the decrease in paging to move from 2GB to 3GB would significantly outweigh the performance increase from a matched pair and my experience thus far with 3GB has confirmed this.&lt;br /&gt;&lt;br /&gt;Blogs to look forward to soon:&lt;br /&gt;&lt;br /&gt;1) The continuation of my series on setting up a glassfish cluster and mysql cluster.&lt;br /&gt;&lt;br /&gt;2) Effectively and safely packing JavaScript files (including Prototype and Scriptaculous) using the pack:tag JSP-Taglib.&lt;br /&gt;&lt;br /&gt;3) My attempts to use HA-JDBC on my glassfish cluster as a way of load balancing requests to the SQL nodes in my MySQL cluster.&lt;br /&gt;&lt;br /&gt;4) Using Quartz for scheduling in Glassfish.&lt;br /&gt;&lt;br /&gt;5) A Glassfish Admin GUI JDBCRealm bug that affects the containers ability to detect and pick up changes to the realm security.&lt;br /&gt;&lt;br /&gt;6) Prototype specific IE6 JavaScript quirks.&lt;br /&gt;&lt;br /&gt;7) Minifying DWR&lt;br /&gt;&lt;br /&gt;8) Clustered second-level caching with JPA and Hibernate in my glassfish cluster.  My 24-hour attempt to use JBoss TreeCache, and the subsequent 30-minute solution using Ehcache.&lt;br /&gt;&lt;br /&gt;... But not necessarily in that order.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-3346724907544541897?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/3346724907544541897/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=3346724907544541897' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3346724907544541897'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3346724907544541897'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2008/01/update-and-things-to-come.html' title='Update and things to come'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-4009408624635234285</id><published>2007-11-09T18:58:00.003-05:00</published><updated>2008-02-19T09:05:29.559-05:00</updated><title type='text'>Project, Part 2: Glassfish cluster</title><content type='html'>I came across a few tutorials, but none of them worked out of the box for me.  So here's my version of how to set up a 2 machine glassfish cluster.  Note, this process was completed on 2 Fedora 7 machines with Glassfish V2 final (aka b58g Promoted Build).  If you're using a different operating system or a different build of glassfish, you might need to tweak the process.&lt;br /&gt;&lt;br /&gt;For the rest of this post, here will be 2 machines: &lt;span style="font-weight: bold;"&gt;glassfish1&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt;glassfish2&lt;/span&gt;. &lt;span style="font-weight: bold;"&gt;Glassfish1&lt;/span&gt; will be the domain administration server (DAS) and will house &lt;span style="font-weight: bold;"&gt;g1-a1&lt;/span&gt; (glassfish1, node-agent 1) as well as &lt;span style="font-weight: bold;"&gt;g1-i1&lt;/span&gt; (glassfish1, instance1).  &lt;span style="font-weight: bold;"&gt;Glassfish2&lt;/span&gt; will only have  &lt;span style="font-weight: bold;"&gt;g2-a1 &lt;/span&gt;(glassfish2, node-agent 1) and &lt;span style="font-weight: bold;"&gt;g2-i1&lt;/span&gt; (glassfish2, instance2).  In all of the commands below, I reference the machines by their hostname (&lt;span style="font-weight: bold;"&gt;glassfish1&lt;/span&gt; or &lt;span style="font-weight: bold;"&gt;glassfish2&lt;/span&gt;).  I am able to do this because I have edited &lt;span style="font-weight: bold;"&gt;/etc/hosts&lt;/span&gt; on both machines to provide a mapping from the hostnames &lt;span style="font-weight: bold;"&gt;glassfish1&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt;glassfish2&lt;/span&gt; to the physical IP for those machines.  If you have not done this, and if you do not have another setup for DNS resolution, you'll need to refer to the machines by their physical IP.&lt;br /&gt;&lt;br /&gt;You'll need to grab the glassfish installer from &lt;a href="https://glassfish.dev.java.net//downloads/v2-b58g.html"&gt;https://glassfish.dev.java.net//downloads/v2-b58g.html&lt;/a&gt; with wget or FTP it onto both &lt;span style="font-weight: bold;"&gt;glassfish1&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt;glassfish2&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;If you don't have Sun's Java JDK 5 or 6 installed on your machines, you'll need that first.  If you're running Fedora, you can find a guide &lt;a href="http://blog.augmentedfragments.com/2007/11/project-part-1-fedora-sun-java.html"&gt;in my post 'Project, Part 1: Fedora + Sun Java'&lt;/a&gt; for installing Sun's Java JDK 1.5 build 13 on Fedora 7.&lt;br /&gt;&lt;br /&gt;Time to setup &lt;span style="font-weight: bold;"&gt;glassfish1&lt;/span&gt;:&lt;br /&gt;&lt;ol&gt;&lt;li&gt; Copy the installer jar file to /opt&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;cp &amp;lt;glassfish-installer&amp;gt;.jar /opt&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Unpack the jar&lt;br /&gt;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;java -Xmx256m -jar &amp;lt;glassfish-installer&amp;gt;.jar&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Add the execute permission to the ant stuff&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;chmod -R +x glassfish/lib/ant/bin&lt;br /&gt;&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Setup the cluster (this will create the DAS)&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;glassfish/lib/ant/bin/ant -f setup-cluster.xml&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Add the glassfish bin directory to the path so we can call &lt;code&gt;asadmin&lt;/code&gt; from anywhere&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;emacs /etc/profile&lt;/code&gt;&lt;/li&gt;&lt;li&gt;In Fedora, there is a nice function &lt;code&gt;pathmunge&lt;/code&gt;, already in &lt;code&gt;/etc/profile&lt;/code&gt;, which will allow you to add directories to the path without accidentally creating duplicates, so add the following line before &lt;code&gt;PATH&lt;/code&gt; is exported:&lt;br /&gt;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;pathmunge /opt/glassfish/bin&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;li&gt;Start the default domain (domain1) with:&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;asadmin start-domain&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Create the first node-agent&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;asadmin create-node-agent --host glassfish1 --port 4848 g1-a1&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Create the cluster&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;asadmin create-cluster --host glassfish1 --port 4848 cluster1&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Create the first instance&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;asadmin create-instance --host glassfish1 --port 4848 --nodeagent g1-a1 --cluster cluster1 g1-i1&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Start the node-agent&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;asadmin start-node-agent --syncinstances=true g1-a1&lt;/code&gt;&lt;/li&gt;&lt;li&gt;Note: the &lt;code&gt;syncinstances&lt;/code&gt; argument will start the associated instances and clusters as well, so you'll want to remember this for later.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ol&gt;If you made it that far, now it's time to setup &lt;span style="font-weight: bold;"&gt;glassfish2&lt;/span&gt;:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;cp &amp;lt;glassfish-installer&amp;gt;.jar /opt&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;java -Xmx256m -jar &amp;lt;glassfish-installer&amp;gt;.jar&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;chmod -R +x glassfish/lib/ant/lib&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;glassfish/lib/ant/bin/ant -f setup-cluster.xml&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Note: the above will create a DAS on &lt;span style="font-weight: bold;"&gt;glassfish2&lt;/span&gt;... which we don't want, so you can remove the DAS after the script finishes with:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;rm -rf /opt/glassfish/domains/domain1&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;If you want, you can avoid creating the DAS on &lt;span style="font-weight: bold;"&gt;glassfish2&lt;/span&gt; in the first place by using the following command instead of the one above (#4):&lt;br /&gt;&lt;code&gt;&lt;br /&gt;glassfish/lib/ant/bin/ant -f setup-cluster.xml \&lt;br /&gt;create-node-agent --Ddas.host=glassfish1&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;That will create a node-agent with the default name "na".  If you want to specify the name of the node-agent, you'll need to edit setup-cluster.xml before running the above command.  Find the property "nodeagent.name" and change the value to whatever name you want:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;property name="nodeagent.name" value="my-nodeagent-name" /&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Either way, running &lt;code&gt;setup-cluster.xml&lt;/code&gt;, which creates the DAS, then removing the DAS and creating a node agent will provide the same result as running &lt;code&gt;setup-cluster.xml&lt;/code&gt; with the option to only create the node-agent.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Add &lt;code&gt;/opt/glassfish/bin&lt;/code&gt; to the &lt;code&gt;PATH&lt;/code&gt; as on &lt;span style="font-weight: bold;"&gt;glassfish1&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Create the second node-agent (If you didn't in step 4)&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;asadmin create-node-agent --host glassfish1 --port 4848 g2-a1&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Create a new instance&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;asadmin create-instance --host glassfish1 --port 4848 --nodeagent g2-a1 --cluster cluster1 g2-i1&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;Start the second node agent&lt;/li&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;asadmin start-node-agent --syncinstances=true g2-a1&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ol&gt;&lt;br /&gt;That's it! Well... sort of, there are a few things to note:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Make sure that the firewall/SELinux/iptables rules are set to allow ports that are needed by Glassfish.  These are, at the minimum:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;4848 (admin HTTP)&lt;/li&gt;&lt;li&gt;8080 (HTTP)&lt;/li&gt;&lt;li&gt;8181 (HTTPS)&lt;/li&gt;&lt;li&gt;8686 (RMI)&lt;/li&gt;&lt;li&gt;3700 (ORB)&lt;/li&gt;&lt;li&gt;7676 (IMQ)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;and possible others if those are already in use.  If your node-agents are having trouble communicating, try disabling the firewall &amp;amp; SELinux, and set iptables to accept all incoming connections.  &lt;span style="font-weight: bold;"&gt;DO NOT DO THIS AS A LONG TERM SOLUTION.  IT IS ONLY MEANT TO RULE OUT PORT COMMUNICATIONS AS A PROBLEM.&lt;/span&gt;  Depending on your needs, you may be altering the port configurations anyway.  I decided to implement a white-listing policy, where I deny all incoming connections except the following:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;On &lt;span style="font-weight: bold;"&gt;glassfish1&lt;/span&gt;:&lt;/li&gt;&lt;ul&gt;&lt;li&gt;connections on port 8080 (for HTTP) from any IP&lt;br /&gt;&lt;/li&gt;&lt;li&gt;connections on port 8181 (HTTPS) from any IP&lt;br /&gt;&lt;/li&gt;&lt;li&gt;connections on port 4848 (HTTP admin GUI) from only my IP&lt;br /&gt;&lt;/li&gt;&lt;li&gt;connections on any port from the IP of glassfish2&lt;/li&gt;&lt;/ul&gt;&lt;li&gt;On &lt;span style="font-weight: bold;"&gt;glassfish2&lt;/span&gt;:&lt;/li&gt;&lt;ul&gt;&lt;li&gt;connections on any port from the IP of glassfish1&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;br /&gt;Thus, my servers are free to communicate with each other on any port they desire, but outside users may only connect to &lt;span style="font-weight: bold;"&gt;glassfish1&lt;/span&gt; on 8080 and 8181, and only I can connect to glassfish1 on 4848. If you're messing with iptables, and you're not too familiar with *nix environments, you should probably have a *nix sys-admin check out your setup before it goes into production to make sure you haven't inadvertently created any security holes.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you're having trouble starting your instances, especially &lt;span style="font-weight: bold;"&gt;g2-i1&lt;/span&gt; on &lt;span style="font-weight: bold;"&gt;glassfish2&lt;/span&gt; you might have to edit your &lt;code&gt;/etc/hosts&lt;/code&gt; file... I did.  This is something that you should check if the following shows up in your &lt;code&gt;server.log&lt;/code&gt;:&lt;br /&gt;&lt;br /&gt;[#|2007-11-05T12:19:03.832-0500|INFO|sun-appserver9.1|javax.ee.enterprise.system.nodeagent|_ThreadID=10;_ThreadName=main;|NAGT&lt;br /&gt;0038:Executing Synchronization for node-agent With DAS|#]&lt;br /&gt;&lt;br /&gt;[#|2007-11-05T12:19:12.920-0500|INFO|sun-appserver9.1|javax.ee.enterprise.system.tools.synchronization|_ThreadID=10;_ThreadNam&lt;br /&gt;e=main;|SYNC001: Unable to communicate with Domain Administration Server. Skipping synchronization.|#]&lt;br /&gt;&lt;br /&gt;[#|2007-11-05T12:19:12.922-0500|SEVERE|sun-appserver9.1|javax.ee.enterprise.system.nodeagent|_ThreadID=10;_ThreadName=main;|NA&lt;br /&gt;GT0035:The NodeAgent failed to complete the intial synchronization with the DAS. Please make sure the DAS is running and is a&lt;br /&gt;ccessible from the NodeAgents server|#]&lt;br /&gt;&lt;br /&gt;[#|2007-11-05T12:19:14.925-0500|WARNING|sun-appserver9.1|javax.ee.enterprise.system.nodeagent|_ThreadID=10;_ThreadName=main;|N&lt;br /&gt;AGT0013:Stopping Node Agent...|#]&lt;br /&gt;&lt;br /&gt;Or, if the following shows up in your instance &lt;code&gt;server.log&lt;/code&gt;:&lt;br /&gt;&lt;br /&gt;[#|2007-11-07T02:25:31.352-0500|WARNING|sun-appserver9.1|javax.jms|_ThreadID=10;_ThreadName=main;_RequestID=efb5ac8e-778b-412b-ae9e-3af1e95a8ee2;|[C4003]: Error occurred on connection creation [localhost:37676]. - cause: java.net.ConnectException: Connection refused|#]&lt;br /&gt;&lt;br /&gt;[#|2007-11-07T02:25:31.363-0500|WARNING|sun-appserver9.1|javax.enterprise.system.stream.err|_ThreadID=10;_ThreadName=main;_RequestID=efb5ac8e-778b-412b-ae9e-3af1e95a8ee2;|java.lang.RuntimeException: MQJMSRA_LB4001: start:Aborted:Unable to ping Broker within 60000 millis (startTimeOut)&lt;br /&gt;at com.sun.messaging.jms.ra.LocalBrokerRunner.start(LocalBrokerRunner.java:366)&lt;br /&gt;at com.sun.messaging.jms.ra.ResourceAdapter.start(ResourceAdapter.java:447)&lt;br /&gt;at com.sun.enterprise.connectors.ActiveInboundResourceAdapter$1.run(ActiveInboundResourceAdapter.java:135)&lt;br /&gt;at java.security.AccessController.doPrivileged(Native Method)&lt;br /&gt;at com.sun.enterprise.connectors.ActiveInboundResourceAdapter.&lt;init&gt;(ActiveInboundResourceAdapter.java:131)&lt;br /&gt;at com.sun.enterprise.connectors.system.ActiveJmsResourceAdapter.&lt;init&gt;(ActiveJmsResourceAdapter.java:247)&lt;br /&gt;at com.sun.enterprise.connectors.ActiveRAFactory.createActiveResourceAdapter(ActiveRAFactory.java:107)&lt;br /&gt;at com.sun.enterprise.connectors.ResourceAdapterAdminServiceImpl.createActiveResourceAdapter(ResourceAdapterAdminServiceImpl.java:300)&lt;br /&gt;at com.sun.enterprise.connectors.ResourceAdapterAdminServiceImpl.createActiveResourceAdapter(ResourceAdapterAdminServiceImpl.java:445)&lt;br /&gt;at com.sun.enterprise.connectors.ConnectorRuntime.createActiveResourceAdapter(ConnectorRuntime.java:224)&lt;br /&gt;at com.sun.enterprise.jms.JmsProviderLifecycle.onStartup(JmsProviderLifecycle.java:428)&lt;br /&gt;at com.sun.enterprise.server.ApplicationServer.onStartup(ApplicationServer.java:442)&lt;br /&gt;at com.sun.enterprise.server.ondemand.OnDemandServer.onStartup(OnDemandServer.java:120)&lt;br /&gt;at com.sun.enterprise.server.PEMain.run(PEMain.java:411)&lt;br /&gt;at com.sun.enterprise.server.PEMain.main(PEMain.java:338)&lt;br /&gt;at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)&lt;br /&gt;at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)&lt;br /&gt;at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)&lt;br /&gt;at java.lang.reflect.Method.invoke(Method.java:585)&lt;br /&gt;at com.sun.enterprise.server.PELaunch.main(PELaunch.java:412)&lt;br /&gt;|#]&lt;br /&gt;&lt;br /&gt;What's happening here is that the node-agent is not starting because the instance is failing due to an MQ startup problem  As it turns out, the MQ needs the hostname to point to an absolute address, not 127.0.0.1 or localhost.  If you edit your &lt;code&gt;/etc/hosts&lt;/code&gt; file on &lt;span style="font-weight: bold;"&gt;glassfish1&lt;/span&gt; to add:&lt;br /&gt;&lt;br /&gt;xxx.xxx.xxx.xxx glassfish1&lt;br /&gt;&lt;br /&gt;And on &lt;span style="font-weight: bold;"&gt;glassfish2&lt;/span&gt; to add:&lt;br /&gt;&lt;br /&gt;xxx.xxx.xxx.xxx glassfish2&lt;br /&gt;&lt;br /&gt;then the problem should go away.  Shalini, was kind enough to point that out to me and he has a blog post &lt;a href="http://blogs.sun.com/technical/entry/troubleshooting_cluster_startup"&gt;here&lt;/a&gt; that also mentions the problem. My original guide for doing this also came from Shalini's blog entry on &lt;a href="http://blogs.sun.com/technical/entry/2_machine_cluster_setup_cli%22"&gt;setting up a 2 machine glassfish cluster using CLI&lt;/a&gt;.&lt;br /&gt;&lt;/init&gt;&lt;/init&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-4009408624635234285?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/4009408624635234285/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=4009408624635234285' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/4009408624635234285'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/4009408624635234285'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2007/11/project-part-2-glassfish-cluster.html' title='Project, Part 2: Glassfish cluster'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-5261867011033729138</id><published>2007-11-07T23:59:00.000-05:00</published><updated>2007-11-08T00:36:38.122-05:00</updated><title type='text'>A response to "Have you seen the news?"</title><content type='html'>&lt;div&gt;I normally reserve this blog for technology posts, but this is an exception.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Over the past day, I've received a couple emails with links to news stories, regarding the tragic death of a British girl studying abroad in Italy, accompanied by the question, "Have you seen this?"  I'm asked because I happened to attend the same high school (within a couple years) as one of the suspects.  Too that question, here is my answer:&lt;br /&gt;&lt;br /&gt;Yes, I've heard, so have you.  Now let's stop endorsing the unrestrained sensationalism of the media.  &lt;br /&gt;&lt;br /&gt;If you browse around for a while, you'll see that various articles paint different pictures slandering different individuals.  In situations like this, what normally is viewed as typical college debauchery is recast as criminally indicative behavior.  After all, profit motivated media needs to sell headlines, and in the realm of "Buy this paper!", nothing beats a sexual deviant with a murderous dark side.  The deplorable part is that it doesn't matter if the individual fits the headline, only that the headline sells the paper.&lt;br /&gt;&lt;br /&gt;Italian police are under enormous pressure, which the strong media attention only increases.  They can't move too quickly, lest they appear hasty or negligent. They can't be too methodical, lest they appear incapable or overwhelmed.  In either case, it is a tightrope to avoid having their qualifications called into question. Too many want the answer now, and too few want the right answer.  It's a terrible situation all around and I feel for each person caught in this dragnet of media attention.  I sympathize with all, each for a unique reason.&lt;br /&gt;&lt;br /&gt;At this point, available information is limited to media sensationalism with underlying culture-stereotypic allegations redolent of long existing cultural partisanship and prejudice.  The most important thing now is to acknowledge the havoc and irreparable change that has and will continue to affect the individuals and their families.  The court of public opinion has no jurisdiction here, and we've all had a time when we deserved more privacy and respect than we received from overly eager interested parties.  If out of nothing more than respect for the families, I hope that the hype will calm down.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And no, I'm not going to post links to any of the articles.  If you have no qualms about supporting sensationalistic media, then I'm sure your Google skills are already top notch.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-5261867011033729138?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/5261867011033729138/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=5261867011033729138' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5261867011033729138'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5261867011033729138'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2007/11/response-to-have-you-seen-news.html' title='A response to &quot;Have you seen the news?&quot;'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-533608562203961120</id><published>2007-11-05T17:46:00.000-05:00</published><updated>2008-01-21T15:05:08.950-05:00</updated><title type='text'>Project, Part 1: Fedora + Sun Java</title><content type='html'>Glassfish needs Java JDK 5, or 6.  Fedora doesn't come with the Sun JDK, but rather GNU Java.  Additionally, unlike my experiences with Ubuntu, Fedora does not have a recent implementation of the JDK available from a repository.  So I was confronted with this choice:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Try to force Glassfish to work with GNU Java, the default in Fedora, or&lt;/li&gt;&lt;li&gt;Try to force Fedora to use Sun's Java&lt;/li&gt;&lt;/ol&gt;I decided on the later after reading that &lt;a href="http://fedoraproject.org/wiki/JavaFAQ#head-dffd888c6f82cfa055b757a3ef865700d0323523"&gt;none of the java language features new in Java 1.5 are implemented yet in gcj&lt;/a&gt;.  At the same time, the blog consensus was that installing Sun's Java on Fedora was not straight forward.  First, the different java versions could interfere with each other; and second, if installed incorrectly, Sun's java could be accidentally, and unintentionally, 'upgraded' to gcj when installing a system update.&lt;br /&gt;&lt;br /&gt;For the most part, I followed a combination of these blogs: &lt;a href="http://liquidat.wordpress.com/2007/05/31/howto-sun-java-on-fedora-7/"&gt;blog1&lt;/a&gt;, &lt;a href="http://www.fedorafaq.org/fc3/custom_java.html"&gt;blog2&lt;/a&gt;, &lt;a href="http://avi.alkalay.net/2007/06/sun-ibm-java-fedora-redhat-mandriva-suse.html"&gt;blog3&lt;/a&gt;, &lt;a href="http://wiki.centos.org/HowTos/JavaOnCentOS"&gt;blog4&lt;/a&gt;.  Alas, none of them worked despite following step by step, but in doing so, I learned a lot about what was going on.&lt;br /&gt;&lt;br /&gt;The first problem seemed to be related to a dependency difference between JDK 1.5.0 build 11 or 12 and JDK 1.5.0 build 13, which is what I was using.  Although each blog listed certain dependencies, none listed the dependency problem I was having.  I still don't know what libXtst is, but without it I was unable to install the JDK.  With it, I was able to install just fine.&lt;br /&gt;&lt;br /&gt;The second problem had to do with the order of installing RPMs.  If I tried to install them all at once with a &lt;code&gt;yum -y --nogpgcheck install /usr/src/redhat/RPMS/i586/java*.rpm&lt;/code&gt;, it would fail because of an error in the SPEC file dealing with font package locations (detailed &lt;a href="http://liquidat.wordpress.com/2007/05/31/howto-sun-java-on-fedora-7/"&gt;here&lt;/a&gt;, in the Current Issues section). However, if I installed only &lt;code&gt;java-1.5.0-sun-1.5.0.13-1jpp.i586.rpm&lt;/code&gt; first, and then installed the rest, I ended up with a working version (though the font problem remained).&lt;br /&gt;&lt;br /&gt;A third problem was that &lt;a href="http://liquidat.wordpress.com/2007/05/31/howto-sun-java-on-fedora-7/"&gt;blog1&lt;/a&gt;, referenced above, which involved rebuilding the java RPMs via &lt;code&gt;rpmbuild -ba java-1.x.0-sun.spec&lt;/code&gt; resulted in a working JRE, but no JDK.&lt;br /&gt;&lt;br /&gt;Here is the final version of what worked for me:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Some prerequisites&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;yum -y install rpmdevtools jpackage-utils rpm-build libXp unixODBC unixODBC-devel libXtst yum-priorities&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;rpm -Uvh http://www.fedorafaq.org/yum http://rpm.livna.org/livna-release-7.rpm&lt;/code&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Get the JPackage Java RPM with&lt;br /&gt;&lt;code&gt;wget http://mirrors.dotsrc.org/jpackage/1.7/generic/SRPMS.non-free/java-1.5.0-sun-1.5.0.13-1jpp.nosrc.rpm&lt;/code&gt; (lists at http://mirrors.dotsrc.org/jpackage/1.7/generic/SRPMS.non-free/)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Download the right JDK binary from Sun (not the self-extracting RPM) for the java version and build version of the JPackage RPM into &lt;code&gt;/usr/src/redhat/SOURCES/&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;recreate the java rpm&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;rpmbuild --rebuild java-1.5.0-sun*src.rpm&lt;/code&gt;&lt;/li&gt;&lt;li&gt;should create new rpms in &lt;code&gt;/usr/src/redhat/RPMS/i586/&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;install new java rpms (need the --nogpg option because locally created rpms are not signed)&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;yum -y --nogpgcheck install /usr/src/redhat/RPMS/i586/java-1.5.0-sun-1.5.0.13-1jpp.i586.rpm&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;yum -y --nogpgcheck install /usr/src/redhat/RPMS/i586/java*.rpm&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;/li&gt;&lt;li&gt;check java version with &lt;code&gt;java -version&lt;/code&gt;, and that the JDK is installed with &lt;code&gt;javac --help&lt;/code&gt;. If the version is not right, or it didn't work, change the default Java system to Sun's with &lt;code&gt;alternatives --config java&lt;/code&gt;, and select the right jvm&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Set the JAVA_HOME environment variable&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Add to &lt;code&gt;/etc/profile&lt;/code&gt;&lt;br /&gt;&lt;code&gt;JAVA_HOME="/usr/lib/jvm/java"&lt;br /&gt;export JAVA_HOME&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;run &lt;code&gt;source /etc/profile&lt;/code&gt; to read in profile, and check it worked with &lt;code&gt;echo $JAVA_HOME&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;That's it, now for Glassfish...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-533608562203961120?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/533608562203961120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=533608562203961120' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/533608562203961120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/533608562203961120'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2007/11/project-part-1-fedora-sun-java.html' title='Project, Part 1: Fedora + Sun Java'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-5053411337054492393</id><published>2007-11-05T17:30:00.000-05:00</published><updated>2007-11-05T17:46:23.215-05:00</updated><title type='text'>Fedora 7 + Sun Java + Glassfish + MySQL</title><content type='html'>&lt;span style="font-weight: bold;"&gt;Start:&lt;/span&gt;&lt;br /&gt;6 - Servers with a base install of Fedora 7&lt;br /&gt;? - Time and effort&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;End result:&lt;/span&gt;&lt;br /&gt;1 - 2 machine, load-balanced, application server cluster with failover and in-memory replication&lt;br /&gt;1 - 4 machine, load-balanced, MySQL NDB database cluster with 2 storage nodes, 1 sql / load balancer node, and 1 management / load balancer node&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The process will be described in a serious of forthcoming posts.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-5053411337054492393?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/5053411337054492393/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=5053411337054492393' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5053411337054492393'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5053411337054492393'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2007/11/fedora-7-sun-java-glassfish-mysql.html' title='Fedora 7 + Sun Java + Glassfish + MySQL'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-5116000476913078677</id><published>2007-10-30T20:48:00.001-04:00</published><updated>2007-10-30T21:15:47.262-04:00</updated><title type='text'>Odd JSP EL behaviour</title><content type='html'>I just discovered an oddity in JSP Expression Language.  I'm a huge fan of EL and its many uses.  This is hardly a problem, but is something to keep in mind because it is an unexpected behavior.&lt;br /&gt;&lt;br /&gt;Take:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;int a = 5;&lt;br /&gt;int b = 2;&lt;br /&gt;&lt;br /&gt;System.out.println(a / b);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The &lt;code&gt;println&lt;/code&gt; statement will output 2.  Thus in EL (and JSTL) in a JSP you would expect the same integer division behavior from the following:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;var="a" value="5" /&amp;gt;&lt;br /&gt;&amp;lt;var="b" value="2" /&amp;gt;&lt;br /&gt;&lt;br /&gt;${a / b}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;In the above, &lt;code&gt;${a / b}&lt;/code&gt; yields 2.5, behaving like floating point division rather than integer division.  I can see this having its uses, but it surprised me, especially since I've been working almost exclusively in Java web technologies for the past few years.  For future purposes I created an EL function so I can just call the following when I want to force integer division (where f is whatever prefix is specified for the custom taglib):&lt;br /&gt;&lt;code&gt;&lt;br /&gt;${f:integerDivision(a,b)}&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-5116000476913078677?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/5116000476913078677/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=5116000476913078677' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5116000476913078677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5116000476913078677'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2007/10/odd-jsp-el-behaviour.html' title='Odd JSP EL behaviour'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-3388052123576730653</id><published>2007-10-29T15:53:00.000-04:00</published><updated>2007-10-29T16:14:19.183-04:00</updated><title type='text'>Why I'm not moving to Leopard</title><content type='html'>Although I've been looking forward to &lt;a href="http://www.apple.com/macosx/"&gt;OS X Leopard&lt;/a&gt; for some time now, I just can't make the switch yet.  As it turns out, there's one major hurdle to be overcome before Leopard is an option for developers of Java EE 5 applications to be deployed on Glassfish.  Here's the problem:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://glassfish.dev.java.net/"&gt;Glassfish&lt;/a&gt; encounters an exception that prevents domain startup when connected to the internet via Wireless, not connected at all, or in any other way not connected by the ethernet port (Stack trace below).  If, however, I'm connected via the ethernet port, everything is okay.  I develop applications to deploy in Glassfish, but I can't limit my development to when I'm tied down by the ethernet cable.  So until there's a solution, I'm sticking with Tiger.&lt;br /&gt;&lt;br /&gt;Anyone with a solution, please let me know.  I haven't seen anything like this since fixing PCs with strange APIPA problems...&lt;br /&gt;&lt;br /&gt;Full Exception:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;[#|2007-10-29T15:28:48.086-0400|SEVERE|sun-appserver9.1|&lt;br /&gt;&amp;nbsp;&amp;nbsp;javax.enterprise.system.core|&lt;br /&gt;&amp;nbsp;&amp;nbsp;_ThreadID=10;&lt;br /&gt;&amp;nbsp;&amp;nbsp;_ThreadName=main;com.sun.enterprise.admin.server.core.JmxConnectorLifecycle@328cbf;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;com.sun.appserv.server.ServerLifecycleException: Cannot bind to URL&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[rmi://169.254.161.207:8686/management/rmi-jmx-connector]:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;javax.naming.ServiceUnavailableException [Root exception is java.rmi.ConnectException:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Connection refused to host: 169.254.161.207; nested exception is:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;java.net.ConnectException: Operation timed out&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;];&lt;br /&gt;&amp;nbsp;&amp;nbsp;_RequestID=69db80be-8146-46bb-a879-157fedee2f87;|&lt;br /&gt;&amp;nbsp;&amp;nbsp;Service com.sun.enterprise.admin.server.core.JmxConnectorLifecycle@328cbf cannot be started!:&lt;br /&gt;&amp;nbsp;&amp;nbsp;com.sun.appserv.server.ServerLifecycleException: Cannot bind to URL   &lt;br /&gt;&amp;nbsp;&amp;nbsp;[rmi://169.254.161.207:8686/management/rmi-jmx-connector]:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;javax.naming.ServiceUnavailableException&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[Root exception is java.rmi.ConnectException:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Connection refused to host: 169.254.161.207; nested exception is:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;java.net.ConnectException: Operation timed out&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;]|&lt;br /&gt;#]&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-3388052123576730653?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/3388052123576730653/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=3388052123576730653' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3388052123576730653'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/3388052123576730653'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2007/10/why-im-not-moving-to-leopard.html' title='Why I&apos;m not moving to Leopard'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-5262847369674541161</id><published>2007-10-25T15:56:00.000-04:00</published><updated>2007-10-25T16:03:38.889-04:00</updated><title type='text'>Facebook Platform</title><content type='html'>Sometimes FB developers suck.  Namely, when I have to add an application just to see what somebody else has done with it.  It's the equivalent annoyance of having to login to comment, or to rate something on a site.  I see the app on their profile, and there are options to interact with it.  I try to do something, but they require me to add the app to do anything.  So, of course, I decline the app and never see what I was trying to see.&lt;br /&gt;&lt;br /&gt;It's just really frustrating that many developers force you to add the app to see anything about it, and it seems a bit backwards.  I'm not going to add an app without seeing inside it first, but if I have to add it to see inside, I'll never add it.  Now, this doesn't seem to be troubling many developers, as they are racking up users quite easily.  Nevertheless, I'd appreciate it if developers would stop trying to build mini walled gardens inside the walled FB estate.&lt;br /&gt;&lt;br /&gt;On a larger note, I'm still waiting for the FB app to beat all FB apps in terms of actual usefulness.  Maybe I just haven't found it yet, but I really don't want gimmicky things on my profile and I think the FB platform has much better uses than trying to build up vampire points.&lt;br /&gt;&lt;br /&gt;On a smaller note, the abundance of crappy looking FB apps being launched has reaffirmed my opinion originally derived from MySpace profiles that most people out there have absolutely zero sense of design.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-5262847369674541161?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/5262847369674541161/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=5262847369674541161' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5262847369674541161'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5262847369674541161'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2007/10/facebook-platform.html' title='Facebook Platform'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-5899008962275671914</id><published>2007-10-25T15:12:00.000-04:00</published><updated>2007-10-25T15:41:14.604-04:00</updated><title type='text'>Gmail IMAP woes</title><content type='html'>I was really excited yesterday when I found out that my Gmail for domains account had IMAP access.  I've used Apple Mail and my Blackjack to access my Gmail accounts via POP for a while now.  It's not that I have anything against the Gmail web interface, but I've just never particularly liked having to open a web browser to view my email. &lt;br /&gt;&lt;br /&gt;POP access had its downfalls, of course, though by using "recent:username@gmail.com" as the username, I was at least able to make sure that all my various devices accessing the account were able to get new emails.  When, I saw IMAP access had been added, I thought all my troubles with accessing Gmail accounts from multiple sources would be gone for good.  Not quite so.&lt;br /&gt;&lt;br /&gt;It's pretty close though, I only have one complaint:  Accessing Gmail via IMAP from Apple Mail stores drafts of messages in Gmails Sent Mail folder, rather than the Drafts folder.  I sent a couple emails last night, and when I woke up this morning I saw 8 copies of one of the emails in my Sent Messages folder.  Before I fired off an apology to the recipient for accidentally spamming their inbox, I figured I'd do a little testing, during the course of which I confirmed that I did not actually send the email 8 times, but rather that drafts were being stored on Gmail as sent messages.&lt;br /&gt;&lt;br /&gt;I don't know whose fault this is, Apple Mail or Gmail, but it's a little annoying.  For now, I've solved the problem by unchecking the box to "Store draft messages on the server" in Apple Mail's preferences for the Gmail account.  I suspect it may have something to do with folder mappings, but I don't know of a way in Apple Mail to change the folder on the server that is associated with a particular type of email, i.e. a draft.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-5899008962275671914?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/5899008962275671914/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=5899008962275671914' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5899008962275671914'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5899008962275671914'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2007/10/gmail-imap-woes.html' title='Gmail IMAP woes'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-5865136064905929959</id><published>2007-10-09T16:15:00.000-04:00</published><updated>2007-10-09T22:15:17.482-04:00</updated><title type='text'>SimpleCaptcha in Glassfish and Tomcat</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_PhxljBBvj_U/Rwvh46r6_nI/AAAAAAAAAAY/mVyr9-VGI6A/s1600-h/Captcha.jpg"&gt;&lt;img style="margin: 0pt 10px 0px 0pt; float: left; cursor: pointer;" src="http://bp2.blogger.com/_PhxljBBvj_U/Rwvh46r6_nI/AAAAAAAAAAY/mVyr9-VGI6A/s320/Captcha.jpg" alt="" id="BLOGGER_PHOTO_ID_5119433769379102322" border="0" /&gt;&lt;/a&gt;I'm a big fan of &lt;a href="http://simplecaptcha.sourceforge.net/"&gt;SimpleCaptcha&lt;/a&gt;.  It has ample configuration options and, after some tweaking, it looks good.&lt;br /&gt;&lt;br /&gt;It's clean and easy for the user to read, but still accomplishes the purpose.  I especially like that you can specify the characters to be used, thereby eliminating possibly confusing characters, such as 1 and l (that's the number one, and a lower case L).&lt;br /&gt;&lt;br /&gt;I've used &lt;a href="http://simplecaptcha.sourceforge.net/"&gt;SimpleCaptcha&lt;/a&gt; in &lt;a href="http://www.fantasycongress.com/"&gt;Fantasy Congress&lt;/a&gt;, running on &lt;a href="http://tomcat.apache.org/download-55.cgi"&gt;Tomcat 5.5&lt;/a&gt;, and &lt;a href="https://glassfish.dev.java.net/"&gt;Glassfish v2&lt;/a&gt;.  Although SimpleCaptcha is great, there were a couple quirks that I had to work around.  By far the best thing about SimpleCaptcha is that the source is available, which I've needed both times to get it to work and which allows you to really change whatever you want.&lt;br /&gt;&lt;br /&gt;1.) If you use SimpleCaptcha on a server running on some version of *nix without a GUI, you'll need to decapitate AWT.  There are a lot of blogs describing how to set the system property java.awt.headless=true, but with limited success.  I never got that to work and from the comments I've seen, I'm not alone.  Another solution, as &lt;a href="http://nussbaum.homeunix.net/page/simplecaptcha-headless-howto"&gt;this blog&lt;/a&gt; describes, is to fake a GUI with the X window interface framebuffer server.  I didn't see any reason to resort to that.  My solution:  I noticed that the problem was being caused in the &lt;code&gt;renderWord(String word, int width, int height)&lt;/code&gt; method of &lt;code&gt;nl.captcha.text.imp.DefaultWordRenderer&lt;/code&gt; at:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();&lt;br /&gt;      &lt;br /&gt;GraphicsDevice gd = ge.getDefaultScreenDevice();&lt;br /&gt;GraphicsConfiguration gc = gd.getDefaultConfiguration();&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The interesting part is that these objects are created, but never referenced, so I just commented them out and it worked fine.  I later discovered an &lt;a href="http://javaboutique.internet.com/tutorials/captcha/index-2.html"&gt;article on Java Boutique&lt;/a&gt; explaining why this was.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_PhxljBBvj_U/Rwvmkqr6_oI/AAAAAAAAAAg/yosCTLfEcz8/s1600-h/Captcha2.jpg"&gt;&lt;img style="margin: 20px 0pt 5px 5px; float: right; cursor: pointer;" src="http://bp1.blogger.com/_PhxljBBvj_U/Rwvmkqr6_oI/AAAAAAAAAAg/yosCTLfEcz8/s320/Captcha2.jpg" alt="" id="BLOGGER_PHOTO_ID_5119438919044890242" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;2.) I didn't like the line across the text that SimpleCaptcha generated by default.  I know that it increases the effectiveness of a captcha but it simply wasn't necessary for my purposes. So I cracked open the source again and removed the line by commenting out these lines in nl.captcha.obscurity.imp.WaterRiple:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;//draw line over the iamge and/or text&lt;br /&gt;NoiseProducer noise = (NoiseProducer)Helper.ThingFactory.loadImpl(&lt;br /&gt;                  Helper.ThingFactory.NOICEIMP, props);&lt;br /&gt;noise.makeNoise(imageDistorted, .1f, .1f, .25f, .25f);&lt;br /&gt;noise.makeNoise(imageDistorted, .1f, .25f, .5f, .9f);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Note: &lt;/span&gt; I set &lt;code&gt;cap.obscurificator &lt;/code&gt;to &lt;code&gt;nl.captcha.obscurity.imp.WaterRiple&lt;/code&gt; in the servlet declaration.  If you're using a different class for obscurity, you'll need to modify the relevant class.&lt;br /&gt;&lt;br /&gt;3.) Dropping the SimpleCaptcha setup I used in Tomcat into Glassfish resulted in a dump of nonsensical text to the display. I remedied this by editing &lt;code&gt;nl.captcha.servlet.CaptchaServlet&lt;/code&gt; and changing the &lt;code&gt;doGet&lt;/code&gt; method from:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;String capText = captchaProducer.createText();&lt;br /&gt;req.getSession().setAttribute(Constants.SIMPLE_CAPCHA_SESSION_KEY, capText);&lt;br /&gt;&lt;br /&gt;String simpleC =(String) req.getSession().getAttribute(Constants.SIMPLE_CAPCHA_SESSION_KEY);&lt;br /&gt;&lt;br /&gt;captchaProducer.createImage(resp.getOutputStream(), capText);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;to:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;String capText = captchaProducer.createText();&lt;br /&gt;req.getSession().setAttribute(Constants.SIMPLE_CAPCHA_SESSION_KEY, capText);&lt;br /&gt;&lt;br /&gt;String simpleC =(String) req.getSession().getAttribute(Constants.SIMPLE_CAPCHA_SESSION_KEY);&lt;br /&gt;&lt;br /&gt;&lt;b&gt;resp.setContentType("image/jpeg");&lt;/b&gt;&lt;br /&gt;captchaProducer.createImage(resp.getOutputStream(), capText);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;It seemed logical that because the servlet is outputting an image (and in my case is mapped to /Captcha.jpg) that the content type should be an image, specifically "image/jpeg".  Because adding the line &lt;code&gt;resp.setContentType("image/jpeg");&lt;/code&gt; solved the problem, I'm led to believe that the contentType was not being set before (though I haven't checked).  However, that presents the interesting question of why SimpleCaptcha works in Tomcat without setting the contentType.  I think this might have something to with the default mime types declared in the server-wide deployment descriptor in Tomcat.  I'm not familiar enough with Glassfish yet to say whether it has an equivalent.  I'd appreciate some insight if you have an explanation.  For now, it works, and I don't have time to troubleshoot what works : )&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8840085855436806841-5865136064905929959?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/5865136064905929959/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=5865136064905929959' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5865136064905929959'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5865136064905929959'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2007/10/simplecaptcha-in-glassfish-and-tomcat.html' title='SimpleCaptcha in Glassfish and Tomcat'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_PhxljBBvj_U/Rwvh46r6_nI/AAAAAAAAAAY/mVyr9-VGI6A/s72-c/Captcha.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8840085855436806841.post-5549646922056889042</id><published>2007-10-09T12:32:00.001-04:00</published><updated>2007-10-09T22:19:24.871-04:00</updated><title type='text'>EIP meet DWR, DWR meet EIP</title><content type='html'>&lt;div style="text-align: left;"&gt;My favorite AJAX framework out there is &lt;a href="http://getahead.org/dwr"&gt;DWR&lt;/a&gt; as I've found it tremendously useful, developer friendly, and a breeze to implement.  I originally used AJAX by writing my own non-abstractable-pseudo-framework and for the longest time I avoided AJAX frameworks because they were not extensible enough, didn't provide the functionality I needed, or just seemed to stick their grubby little paws into my code in ways I didn't desire.  I finally adopted a framework when I discovered DWR, and it was my most wonderful day with AJAX.  Thus, I've become quite attached to DWR.&lt;br /&gt;&lt;br /&gt;I've toyed around with edit-in-place functionality in a few projects, but only on a small scale, and without using any 3rd party scripts.  (I generally approach technologies in that manner, I like to know how it works before I start using it.)  I wanted to thoroughly integrate an in-place editing system in a project I'm currently developing, and since by now I had experimented enough to know how the edit-in-place wheel rolls, I started looking around for a suitable wheel that somebody else had already built.  The best I stumbled across is &lt;a href="http://joseph.randomnetworks.com/archives/2006/08/28/edit-in-place-version-033/"&gt;EIP&lt;/a&gt;, written by Joseph Scott (check out his blog at &lt;a href="http://joseph.randomnetworks.com/"&gt;http://joseph.randomnetworks.com/&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;EIP seemed to be a nice system, but it had one obstacle that needed to be overcome for it to be a viable option for me:  it uses Prototype for AJAX, and I wanted DWR integration.  Fortunately, this was relatively straight forward and there were no huge barriers to making DWR and EIP play well together.  The only stumbling block was how to enable EIP to call methods opened up by DWR with variable numbers of arguments.  A cool thing about javascript is that essentially every method accepts a variable number of arguments, despite what the method header (which is basically just a way to assign names to arguments) may declare.   The tricky part was that somehow, EIP needed to be able to take:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;dwr_params: {&lt;br /&gt;p0: 0,&lt;br /&gt;p1: 1&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;dwr_params: {&lt;br /&gt;p0: 0,&lt;br /&gt;p1: 1,&lt;br /&gt;p2: 2&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;and then call:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;DwrExposedClass.dwrExposedMethod(p0,p1,dwrCallback);&lt;br /&gt;DwrExposedClass.dwrExposedMethod(p0,p1,p2,dwrCallback);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;respectively.  But there is no way to construct an argument list in a loop, except for to create the entire method call as a String and then use eval() to make it happen.  I'm not a big fan of eval() (for reasons that may show up in a different post), and although I occasionally see it as necessary, I only resort to it after exhausting the possibility of other options.&lt;br /&gt;&lt;br /&gt;The first thing that came to mind was currying, which (for the unfamiliar) lets you turn a method that takes multiple arguments into a method that takes one argument.  This seemed like a good starting point because if I could call any DWR exposed method with only one argument, then it didn't matter how many additional arguments needed to be included.  At that point I figured I could either curry an array of arguments down to one, or recursively curry until I arrived at a single argument method.&lt;br /&gt;&lt;br /&gt;When playing around with how to implement such a solution, I discovered a much simpler way in rediscovering the apply() method of the javascript Function object.  Essentially, arguments are passed as an array in javascript, evidenced by the fact that any number of arguments can be passed to any method, even one with zero declared parameters, and then referenced within the method by treating the local variable &lt;code&gt;arguments&lt;/code&gt; as an array.  Thus, at a basic level, the apply() method of a function can be used to apply an array of arguments to the function.&lt;br /&gt;&lt;code&gt;&lt;br /&gt;var args = [0,1,2,3];&lt;br /&gt;DwrExposedClass.dwrExposedMethod.apply({},args);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Problem solved, I can now make any item editable and use DWR for AJAX with:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;EditInPlace.makeEditable({&lt;br /&gt; id: 'editableItemId',&lt;br /&gt; ... any other EIP params...&lt;br /&gt; use_dwr: true,&lt;br /&gt; dwr_method: DwrExposedClass.dwrExposedMethod,&lt;br /&gt; dwr_params: { // any other data you want to pass&lt;br /&gt;     p0: 0,&lt;br /&gt;     p1: 'foo'&lt;br /&gt; }&lt;br /&gt;});&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Feel free to use the script.  You'll need DWR and Prototype.&lt;br /&gt;&lt;br /&gt;Download the script &lt;a href="http://ryanwilson.m.googlepages.com/EditInPlace.js"&gt;here&lt;/a&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/8840085855436806841-5549646922056889042?l=blog.augmentedfragments.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.augmentedfragments.com/feeds/5549646922056889042/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8840085855436806841&amp;postID=5549646922056889042' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5549646922056889042'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8840085855436806841/posts/default/5549646922056889042'/><link rel='alternate' type='text/html' href='http://blog.augmentedfragments.com/2007/10/eip-meet-dwr-dwr-meet-eip.html' title='EIP meet DWR, DWR meet EIP'/><author><name>Ryan</name><uri>http://www.blogger.com/profile/12120844691062261364</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_PhxljBBvj_U/SP1ldiIKqEI/AAAAAAAAACI/C5kH5YLttvU/S220/rw_headshot.jpg'/></author><thr:total>2</thr:total></entry></feed>
