tag:blogger.com,1999:blog-1291097130614483152024-02-06T21:59:09.891-05:00Joe Sondow's BlogJoe Sondowhttp://www.blogger.com/profile/09328659376615417083noreply@blogger.comBlogger11125tag:blogger.com,1999:blog-129109713061448315.post-51322140850378831742012-11-11T19:12:00.002-05:002012-11-11T21:29:43.909-05:00State Is a Bug<br />
<h3>
The story so far</h3>
<br />
For those listening in on this public conversation, here's the background.<br />
<br />
I participated in a <a href="http://javaposse.com/java-posse-392-roundup-12-continuous-deployment">podcast episode about Continuous Deployment at the Java Posse Roundup 2012</a>.<br />
<br />
<a href="https://twitter.com/marcesher/status/265076269316714496">Marc Esher tweeted about the podcast</a>, asking me to elaborate further on my assertion that "state is a bug".<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikI95hmNTll0uMgjO8Tn67fCP1I1Go7EJxyT4kcRkCdBOiIcZI_LCjuha6ASk2LyFfwRehIOP3FvYLZ7qTuQZfdRUGKFoN6uDlNoykJ5TLeqoXRj-5O96G0GWETfpoQxOPRPuQ8zgVh04/s1600/marc-esher-twitter.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikI95hmNTll0uMgjO8Tn67fCP1I1Go7EJxyT4kcRkCdBOiIcZI_LCjuha6ASk2LyFfwRehIOP3FvYLZ7qTuQZfdRUGKFoN6uDlNoykJ5TLeqoXRj-5O96G0GWETfpoQxOPRPuQ8zgVh04/s1600/marc-esher-twitter.png" /></a></div>
<br />
<br />
That led to a longer <a href="https://gist.github.com/4046012">question on a GitHub gist</a>:<br />
<blockquote class="tr_bq">
<blockquote class="tr_bq">
Joe, thanks for responding.</blockquote>
<blockquote class="tr_bq">
I'm most interested in what are perhaps pedestrian issues, but they're issues (for me) nonetheless. You mentioned "outsourcing" session management... where can I read more on that?</blockquote>
<blockquote class="tr_bq">
Take the simple case of: I'm a user on your system. I'm logged in. I'm doing things. The server I'm on disappears while the screen I'm reading is currently loading.</blockquote>
<blockquote class="tr_bq">
What happens? Do I see an error? Am I sent to a new machine, with all my session state in tact, and the screen I was reading simply reloads?</blockquote>
<blockquote class="tr_bq">
Or take perhaps a different architecture where a machine isn't brought down until all its users have been successfully moved to other servers. For example, perhaps our configuration is such that we have N instances, and we do a rolling code push to those instances. User A is on Server 5, which has the old code. We need to get that user to Server 2, which has the new code, along with all of his state. Once all users are off of Server 5, the deploy then moves to Server 5 as well and then Server 5 is brought back into commission and accepts new requests. </blockquote>
<blockquote class="tr_bq">
So: what techniques, processes, and tools support these practices?</blockquote>
<blockquote class="tr_bq">
Thanks again.</blockquote>
</blockquote>
My response got a bit long for a gist, so here it is as a blog post. Marc and I haven't met. This is just how the internet works now.<br />
<br />
<h3>
My response</h3>
<br />
Hi Marc,<br />
<br />
Here's how I see it.<br />
<br />
<h4>
Instantaneous failure</h4>
<br />
As for the question of instantaneous failure, the server shouldn't be expected to disappear while it's handling a user's request and delivering a quick response, unless the instance truly has catastrophic power failure in that moment. In that case there's no way I know of to recover. However, I'm not really concerned with that case because I think it's sufficiently rare, not currently practical to protect against, and not significant yet, because clients still occasionally expect to do three retries before succeeding. During an electrical storm or "Take your monkey to work"-day, servers can end up getting unplugged, and clients will be affected if their requests occurred in the right instant, but I don't think that problem has been solved well yet. The solvable problem is, when the user retries and gets a healthy server's response, will the user have an appropriate experience, or will they lose all their work and need to redo all their recent actions?<br />
<br />
So, for the duration of one request and response in a stateless protocol like HTTP, the client and server are dependent on each other, and on the internet hops between them. Keep that request-response duration short enough, and the risk of instantaneous power loss should be mitigated.<br />
<br />
If an instance gets a software shut-down signal, it ought to be configured to shut down its web server process gracefully before permitting the operating system to shut down. Web server graceful shut down should mean refusing to accept new requests, while finishing the delivery of responses the server has already started. Therefore, "terminate instance" should usually entail automatic draining of connections before total shut down occurs.<br />
<br />
<h4>
Session state</h4>
<br />
The state that I consider a bug in any highly available system is session state, not request state. Anything you want to store in session could instead be stored in a remote shared state service such as a database, a queue, or a workflow service. Consider a case for a mature shopping site like Amazon.com or eBay.com. If you log in to your account with two laptops, and start viewing items on both laptops, the history of what you've recently viewed is generally visible to both sessions around the same time, even though you are collecting history in two different sessions, possibly on two different servers. This means that the important shopping transactions of "view item" are stored in a database shared by the two web servers your clients are talking to. If one of those servers shuts down and both of your laptops continue sending shopping requests to view more items, then some of the client traffic should get switched by a load balancer to a healthy server for the future requests. The state of the user's shopping sessions is undamaged because it is still in the shared database, visible to all server instances.<br />
<br />
Once you move "session"-type state out of the server's session context and into a remote shared service, it becomes practical to set up auto-scaling policies to get more stateless virtual servers when you need them, and to terminate stateless virtual servers when your traffic levels drop, in order to save money when renting virtual servers from a cloud provider.<br />
<br />
I think about this stuff a lot because I work on Asgard, the open source app deployment and cloud management app produced and used by Netflix. I talk to a lot of Netflix engineers about the need to get state out of their applications. I'm also working to get state out of Asgard itself.<br />
<br />
<h4>
Rehearsing for failure</h4>
<br />
Many Netflix services use AWS auto scaling for availability and cost savings, so they need to keep user state out of their service so servers can be used interchangeably by clients. Netflix also uses Chaos Monkey to terminate instances within an Auto Scaling Group daily during business hours just to make sure the developers are still maintaining a system that is resilient to small-scale instance failures. Amazon and all other data centers have server failures, so our best defense is to expect failures and to plan for them, and to practice automatic recovery all the time. Netflix doesn't use auto scaling for Cassandra database rings because Cassandra doesn't have a good way to handle frequent growth in the size of a cluster. However, we do use Chaos Monkey to exercise Cassandra's ability to recover completely from an occasional single instance termination.<br />
<br />
We also have all our services in three AWS Availability Zones (data centers) so if one zone has a major problem, Netflix is generally unaffected while Reddit and Pinterest are sometimes down for hours.<br />
<br />
<h4>
Asgard's state problem</h4>
<br />
On a more personal level, I'm working to remove state from Asgard so that I can increase Asgard's server count and release new versions of Asgard for use by Netflix engineers more frequently and conveniently. My intent is to use Amazon Simple Workflow Service to store the state of each long-running automation process that gets started by an Asgard user. For example, a complex rolling push of new code to replacement instances in an auto scaling group, or a reversible push of new code to a new auto scaling group, with automated result checking and rollback on failure. The state of the long-running workflow execution will then be visible to all Asgard instances, while none of those instances needs to stay up just to finish the automation process.<br />
<br />
<h3>
Related Netflix tech blog posts</h3>
<br />
<ul>
<li><a href="http://techblog.netflix.com/2012/10/post-mortem-of-october-222012-aws.html">Post-mortem of October 22,2012 AWS degradation</a></li>
<li><a href="http://techblog.netflix.com/2012/09/eureka.html">Netflix Shares Cloud Load Balancing And Failover Tool: Eureka!</a></li>
<li><a href="http://techblog.netflix.com/2012/07/chaos-monkey-released-into-wild.html">Chaos Monkey Released Into The Wild</a></li>
<li><a href="http://techblog.netflix.com/2012/07/lessons-netflix-learned-from-aws-storm.html">Lessons Netflix Learned From The AWS Storm</a></li>
<li><a href="http://techblog.netflix.com/2012/06/scalable-logging-and-tracking.html">Scalable Logging and Tracking</a></li>
<li><a href="http://techblog.netflix.com/2012/06/asgard-web-based-cloud-management-and.html">Asgard: Web-based Cloud Management and Deployment</a></li>
<li><a href="http://techblog.netflix.com/2012/06/netflix-operations-part-i-going.html">Netflix Operations: Part I, Going Distributed</a></li>
<li><a href="http://techblog.netflix.com/2012/02/fault-tolerance-in-high-volume.html">Fault Tolerance in a High Volume, Distributed System</a></li>
<li><a href="http://techblog.netflix.com/2012/01/auto-scaling-in-amazon-cloud.html">Auto Scaling in the Amazon Cloud</a></li>
<li><a href="http://techblog.netflix.com/2011/12/making-netflix-api-more-resilient.html">Making the Netflix API More Resilient</a></li>
<li><a href="http://techblog.netflix.com/2011/11/benchmarking-cassandra-scalability-on.html">Benchmarking Cassandra Scalability on AWS - Over a million writes per second</a></li>
<li><a href="http://techblog.netflix.com/2011/07/netflix-simian-army.html">The Netflix Simian Army</a></li>
<li><a href="http://techblog.netflix.com/2011/04/lessons-netflix-learned-from-aws-outage.html">Lessons Netflix Learned from the AWS Outage</a></li>
<li><a href="http://techblog.netflix.com/2011/03/nosql-netflix-talk-part-1.html">NoSQL @ Netflix Talk (Part 1)</a></li>
<li><a href="http://techblog.netflix.com/2011/01/nosql-at-netflix.html">NoSQL at Netflix</a></li>
<li><a href="http://techblog.netflix.com/2010/12/four-reasons-we-choose-amazons-cloud-as.html">Four Reasons We Choose Amazon’s Cloud as Our Computing Platform</a></li>
<li><a href="http://techblog.netflix.com/2010/12/5-lessons-weve-learned-using-aws.html">5 Lessons We’ve Learned Using AWS</a></li>
</ul>
<br />
<br />Joe Sondowhttp://www.blogger.com/profile/09328659376615417083noreply@blogger.com0tag:blogger.com,1999:blog-129109713061448315.post-37830472100750042212012-02-07T17:06:00.014-05:002012-02-20T17:21:18.472-05:00Ye Olde Tragic Journey of Attempting to Upgrade to Grails 2.0.0It appears that Grails 2.0.0 is not yet ready for the large, pre-existing Grails 1.3.7 application my team works on at Netflix.<br />
<br />
First a little background. I work on Asgard, formerly known as the Netflix Application Console (NAC). Here's <a href="http://www.slideshare.net/joesondow/building-cloudtoolsfornetflix-9419504">a slide deck</a> and <a href="http://blip.tv/silicon-valley-cloud-computing-group/building-cloud-tools-for-netflix-5754984">a video</a> about it. Asgard is a Grails-based web app used internally by Netflix to manage cloud systems and deployments in the Amazon Web Services cloud. If everything goes as planned I will be open sourcing Asgard under the Apache license on Netflix's Github space later in 2012. The application has been under constant development and in general use within Netflix since early 2010. Any time Asgard has a major problem, engineers at Netflix cannot deploy their changes to production and cannot run experiments in our test environments. If you've streamed a Netflix video in the past year, the servers that show you the user interface components, grant you access to stream the video, and store your viewing history and ratings were all deployed and upgraded repeatedly using Asgard. (The video file itself comes from a Content Delivery Network (CDN), but let's not get into that.)<br />
<br />
I want to upgrade Grails. Here are a few reasons. The new error page shows the code where the error occurred. Taglibs can use GSPs instead of Groovy strings for templating. The build system provides easy overriding of the ivy repository location, and provides hooks to remove Asgard's dependence from the Netflix build system so I can open source Asgard. Testing annotations instead of inheritance makes for better Spock tests. Plugins can be packaged as jars and retrieved from an Artifactory repository instead of a directory filled with files. More classes are supposed to be redeployed to a running server during development iterations. It all sounds great.<br />
<br />
I strive for the cleanest user experience I can achieve. That includes the visual and REST API of the application for users, as well as the quality of the code for my fellow developers, and the email messages and logs from the server. Let me say that last part again. The logs from the server. The logs are a crucial part of the interface between my team and the Grails framework. If the signal-to-noise ratio in the logs is too low, then my team becomes dangerously trained to ignore warnings and errors in their own IDE. I consider that risk to be well worth avoiding.<br />
<br />
In trying to upgrade Grails 2.0.0 I encountered a number of problems. For each problem I spent time investigating the details and looking for solutions. This blog entry shows the issues that I experienced. I've divided them into a few good changes that help me find mistakes in my application, some annoying problems that are endurable for the short term, and significant regressions that make Grails 2.0.0 substantially worse for me than Grails 1.3.7.<br />
<div>
<br /></div>
<span style="font-size: x-large;">Good</span><br />
<br />
<span style="font-size: large;">Compilation errors in GSPs</span><br />
<br />
Solution:<br />
Remove copy-pasted code that probably never would have worked anyway.<br />
<br />
<span style="font-size: large;">TLD start up messages are gone from log</span><br />
<br />
Grails 1.3.7 always wrote this in the start up log on my local machine and I was never able to configure the logging early enough in the start up process to eliminate these messages.<br />
<br />
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;">2012-02-07 10:25:39,605 [main] INFO digester.Digester - TLD skipped. URI: <span class="s1">http://www.springframework.org/tags</span> is already defined</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;">2012-02-07 10:25:39,629 [main] INFO digester.Digester - TLD skipped. URI: <span class="s1">http://java.sun.com/jsp/jstl/core</span> is already defined</span></div>
<div class="p1">
<span style="font-family: 'Courier New', Courier, monospace;">2012-02-07 10:25:39,648 [main] INFO digester.Digester - TLD skipped. URI: <span class="s1">http://java.sun.com/jsp/jstl/fmt</span> is already defined</span><br />
<br /></div>
<div class="p1">
Grails 2.0.0 no longer shows those useless messages, so that's good.</div>
<br />
<span style="font-size: x-large;">Annoying</span><br />
<br />
<span style="font-size: large;">Subclassing AbstractList causes error</span><br />
<br />
During command line unit test execution, and during application runtime this error occurs on a class that extends AbstractList.<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">[exec] | Error Compilation error compiling [unit] tests: (class: com/netflix/nac/push/Cluster, method: super$1$clearErrors signature: ()V) Illegal use of nonvirtual function call (Use --stacktrace to see the full trace)</span><br />
<br />
<div>
I tried running unit tests in the IDE in an attempt to debug the issue, but that resulted in unit tests hanging without easily uncovered explanations, so I'll skip over the detailed reasons why this is happening and just accept that I can't subclass AbstractList. It wasn't strictly necessary for my use case. It was just a nice-to-have. Not being able to subclass AbstractList in a Groovy class is a bummer and probably a regression but not a deal breaker for me.<br />
<br />
Solution:<br />
Don't subclass AbstractList anymore. This might be a deal breaker for other applications.<br />
<br /></div>
<span style="font-size: large;">Spock plugin for Grails 2.0.0 has no stable release yet</span><br />
<br />
<a href="http://grails.org/plugin/spock">http://grails.org/plugin/spock</a> shows that Spock tests in Grails 2.0.0 require a SNAPSHOT version of the Spock plugin. This means that each time my project builds it could get different Spock framework code. It also means that the maintainers of Spock do not yet regard the new plugin as being fully ready for release. My project has enough Spock unit tests that this is a pretty serious trade off.<br />
<br />
Solution:<br />
None found.<br />
<div>
<br /></div>
<span style="font-size: large;">Immutable annotation errors</span><br />
<br />
I knew this one from online discussions and conferences. Grails 1.3.7 uses an older Groovy version where the groovy.lang.Immutable annotation is the only Immutable annotation available, and it's deprecated because it's a bit buggy. Grails 2.0.0 uses a later version of Groovy where the default groovy.lang.Immutable is deprecated and the improved annotation is groovy.transform.Immutable. Unfortunately there is no trivial way to make the deprecated Immutable be non-default or illegal, so everyone on the team needs to be aware of this risk for every new Immutable class. Maybe my team will eventually make a CodeNarc rule to outlaw references to the default Immutable class.<br />
<br />
Solution:<br />
Add this line to every source file that mentions @Immutable<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">import groovy.transform.Immutable</span><br />
<br />
<span style="font-size: large;">Checking for equality between a null Integer to an integer literal now throws an exception instead of returning false</span><br />
<pre></pre>
<pre><span style="font-family: 'Courier New', Courier, monospace;">Integer responseCode = checkResponse()
if (responseCode == 200) { // If responseCode is null this throws an exception in Groovy 1.8.4 in Grails 2.0.0
proceed()
} else {
flash.message = "Error: the response code was ${responseCode}"
}</span>
</pre>
<br />
<a href="http://schneide.wordpress.com/2012/01/23/upgrading-your-app-to-grails-2-0-0-better-wait-for-2-0-1/">This other blog</a> suggests that this might be a problem with checking for equality with null values for multiple cases other than Integer. If that turns out to be the case for anything in my large application then I will have to consider this a regression and definitely not a deliberate change to Groovy. I thought maybe this was a side effect of Groovy 1.8.4's increased compiler strictness but the other blog suggests that it might just be a bug in either Groovy 1.8.4 or Grails 2.0.0. This might be a show stopper, since I often need to compare values to null variables.<br />
<br />
Solution:<br />
Test every case in the application and replace all the Integer variables with int variables. Refactor all cases where null used to be a useful sentinel value, and come up with a different sentinel value like -1, but write more code to check for the sentinel value and convert it to a better string like "null" or "missing" or "failed" for the resulting error strings.<br />
<br />
<pre><span style="font-family: 'Courier New', Courier, monospace;">int responseCode = checkResponse()
if (responseCode == 200) {
proceed()
} else {
flash.message = "Error: the response code was ${responseCode == -1 ? 'null' : responseCode }"
}</span>
</pre>
<pre><span style="font-family: 'Courier New', Courier, monospace;">
</span></pre>
<div>
This isn't a good solution but it might be acceptable.<br />
<br /></div>
<span style="font-size: large;">Interactive mode is the default</span><br />
<br />
This may be nice when building new apps on the command line, but it's mostly a productivity hurdle when I have automated builds that need an extra start up parameter, and a dozen IDE start up configuration buttons on each developer machine, all of which need a new start up parameter. Adding a start up parameter might sound like a little thing, but actually this upgrade has required 6 new start up parameters so far just to make things work like they used to. See the end of this blog post for details. That's a lot of new cruft to manage for all of Netflix's Grails developers and builds.<br />
<br />
--non-interactive<br />
-server<br />
-javaagent:/Users/jsondow/w1/Tools/groovy/grails-2.0.0/lib/com.springsource.springloaded/springloaded-core/jars/springloaded-core-1.0.2.jar<br />
-noverify<br />
-Dspringloaded=profile=grails<br />
-Dgrails.log.deprecated=false<br />
<br />
Solution:<br />
Repetitively add the <span style="font-family: 'Courier New', Courier, monospace;">--non-interactive</span> command line parameter to all IntelliJ configurations and all ant targets<br />
<div>
<br /></div>
<span style="font-size: large;">Repeated statements in start up log</span><br />
<br />
The start-up log in IntelliJ is now needlessly repetitive, hurting the signal-to-noise ratio of the log. This impacts my ability to know at a glance if a legitimate new problem has occurred.<br />
Four distinct statements now take sixteen lines each time I start my app from IntelliJ which I sometimes need to do many times a day. Adding animated dots to a log string to show the passage of time for newbie command line users should not be worth the trade off of adding garbage to the start up log for long-time developers of mature applications.<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">| Configuring classpath</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Configuring classpath.</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Environment set to development</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Environment set to development.</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Environment set to development..</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Environment set to development...</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Environment set to development....</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Environment set to development.....</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Packaging Grails application</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Packaging Grails application.</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Packaging Grails application..</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Packaging Grails application...</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Config: development environment</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Packaging Grails application....</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Packaging Grails application.....</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Config: development environment</span><br />
<br />
A little research shows the naive solution to be adding the <span style="font-family: 'Courier New', Courier, monospace;">-plain-output</span> command line parameter to the command line string (NOT to the VM parameters string) in the IntelliJ config. That makes the logs shorter and more reasonable. However, doing this breaks IntelliJ's ability to launch a browser with the localhost:8080 home page when ready. I've set up my home page to trigger the loading of all other asynchronous cache loading service initializations, so starting up this way would require a click-wait-copy-paste-browse-wait manual procedure for each server restart in development mode. This is even worse than a noisy log, so this solution doesn't work for me.<br />
<br />
Solution:<br />
None found.<br />
<br />
<span style="font-size: x-large;">Regressions</span><br />
<br />
<span style="font-size: large;">printf now adds extra newline characters incorrectly between string tokens and variable tokens</span><br />
<pre></pre>
<br />
<pre><span style="font-family: 'Courier New', Courier, monospace;">printf(" Cached %5d '%s'\n", map.size(), name)</span></pre>
<br />
That's my one use of printf. It's great for keeping my cache loading log messages vertically lined up. The output used to look like this:<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;"> Cached 587 'us-east-1 Security Groups'</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> Cached 32 'us-east-1 DB Snapshots'</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> Cached 38 'us-east-1 DB Instances'</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
<div>
Now that same line of code results in this output:<br />
<br /></div>
<div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"></span><br />
<div>
<span style="font-family: 'Courier New', Courier, monospace;"></span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> Cached </span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> 587</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> '</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">us-east-1 Security Groups</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">'</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> Cached </span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> 32</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> '</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">us-east-1 DB Snapshots</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">'</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> Cached </span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> 38</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> '</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">us-east-1 DB Instances</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">'</span></div>
<span style="font-family: 'Courier New', Courier, monospace;"></span><br />
<div>
<div style="font-family: Times;">
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><span style="font-family: 'Courier New', Courier, monospace;"></span></span><br />
<div>
<div style="font-family: Times;">
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><span style="font-family: 'Courier New', Courier, monospace;"><span style="font-family: 'Courier New', Courier, monospace;">
</span></span></span><br />
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><span style="font-family: 'Courier New', Courier, monospace;"><span style="font-family: 'Courier New', Courier, monospace;"><br /></span></span></span></div>
<span style="font-family: 'Courier New', Courier, monospace;"><span style="font-family: 'Courier New', Courier, monospace;"><span style="font-family: 'Courier New', Courier, monospace;">
</span></span></span></div>
</div>
</div>
<span style="font-family: 'Courier New', Courier, monospace;"><span style="font-family: 'Courier New', Courier, monospace;">
</span></span></div>
</div>
</div>
<span style="font-family: 'Courier New', Courier, monospace;">
</span></div>
</div>
Solution:<br />
Don't use printf anymore. Use String.format() separately for each variable that needs padding. Stick the result into a GString. This bug is not a deal breaker for me but it might be for some applications that use printf more extensively.<br />
<br />
<pre><span style="font-family: 'Courier New', Courier, monospace;">println(" Cached ${String.format('%5d', map.size())} '${name}'")</span>
</pre>
<br />
<span style="font-size: large;">Using a mixin causes infinite recursive calls</span><br />
<br />
Infinite recursive call from using a mixin on a Java library class, resulting in a StackOverflowException each time the server starts.<br />
<br />
| Error 2012-02-06 16:37:27,155 ["http-bio-8080"-exec-10] ERROR errors.GrailsExceptionResolver - StackOverflowError occurred when processing request: [GET] /us-east-1/autoScaling/list<br />
Stacktrace follows:<br />
Message: Executing action [list] of controller [com.netflix.nac.AutoScalingController] caused exception: Runtime error executing action<br />
Line | Method<br />
->> 886 | runTask in java.util.concurrent.ThreadPoolExecutor$Worker<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
| 908 | run in ''<br />
^ 680 | run . . in java.lang.Thread<br />
<br />
Caused by ControllerExecutionException: Runtime error executing action<br />
->> 886 | runTask in java.util.concurrent.ThreadPoolExecutor$Worker<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
| 908 | run in ''<br />
^ 680 | run . . in java.lang.Thread<br />
<br />
Caused by InvocationTargetException: null<br />
->> 886 | runTask in java.util.concurrent.ThreadPoolExecutor$Worker<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
| 908 | run in ''<br />
^ 680 | run . . in java.lang.Thread<br />
<br />
Caused by StackOverflowError: null<br />
->> 60 | get in org.codehaus.groovy.util.AbstractConcurrentMap$Segment<br />
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -<br />
^ 30 | get in org.codehaus.groovy.util.AbstractConcurrentMap<br />
<br />
org.codehaus.groovy.grails.web.errors.GrailsWrappedRuntimeException<br />
Caused by: org.codehaus.groovy.grails.web.servlet.mvc.exceptions.ControllerExecutionException: Executing action [list] of controller [com.netflix.nac.AutoScalingController] caused exception: Runtime error executing action<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>at java.lang.Thread.run(Thread.java:680)<br />
Caused by: org.codehaus.groovy.grails.web.servlet.mvc.exceptions.ControllerExecutionException: Runtime error executing action<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>... 3 more<br />
Caused by: java.lang.reflect.InvocationTargetException<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>... 3 more<br />
Caused by: java.lang.StackOverflowError<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>at org.codehaus.groovy.util.AbstractConcurrentMap$Segment.get(AbstractConcurrentMap.java:60)<br />
<span class="Apple-tab-span" style="white-space: pre;"> </span>at org.codehaus.groovy.util.AbstractConcurrentMap.get(AbstractConcurrentMap.java:30)<br />
<br />
The error message is entirely unhelpful. Debugging eventually pointed to a mixin class with no obvious problems my team could discern.<br />
<br />
Solution:<br />
For now, remove all use of mixins and go back to a clunkier metaclass syntax for monkey patching classes. This is a large step backward. Maybe there is something else going on, but this would fix the problem if I chose to proceed with the upgrade right now.<br />
<br />
<span style="font-size: large;">Out of memory exception during build</span><br />
<br />
I habitually avoid using any plugins I can live without for the sake of easier upgrades and reduced detective work in the face of memory problems and mysterious errors. However, other contributors to the application have added some plugins they wanted to try, although the use of the plugin never got finished enough to justify its continued presence.<br />
<br />
Solution:<br />
Delete all plugins and references to them for now. When I need to add some back I'll increase the memory allocated to the build process. This isn't a suggestion for other people, but it's the simplest band-aid for the problem in my particular application while I continue to experiment with Grails 2.0.0.<br />
<br />
<span style="font-size: large;">Unhelpful warnings in the start up logs about Grails calling deprecated Grails methods</span><br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">jsondow@lgmac-jsondow:~/w1/webapplications/nac/main$ grails run-app --non-interactive</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Packaging Grails application...</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Config: development environment</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Packaging Grails application.....</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Config: development environment</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">2012-01-30 11:14:25,445 [main] WARN util.GrailsUtil - [DEPRECATED] Method ApplicationHolder.setApplication(application) is deprecated and will be removed in a future version of Grails.</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Running Grails application</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">2012-01-30 11:14:30,124 [Thread-9] WARN util.GrailsUtil - [DEPRECATED] Method ApplicationHolder.setApplication(application) is deprecated and will be removed in a future version of Grails.</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">2012-01-30 11:14:35 PST EmailerService Initializing...</span><br />
<div>
<br /></div>
<div>
Warnings in the start up log are important. If my code is doing something hazardous I want to know about it and solve it. Spurious framework warnings add dangerous noise to my start-up logs, increasing the risk that people on my team will ignore important messages in the log. Grails 2.0.0 unhelpfully warns me that the framework is calling its own deprecated methods, and there appears to be nothing I can do about it.<br />
<div>
<br /></div>
</div>
<div>
My first approach was to change the logging level to <span style="font-family: 'Courier New', Courier, monospace;">error</span> for the amusingly named <span style="font-family: 'Courier New', Courier, monospace;">grails.util.GrailsUtil</span> class. However, reading the <a href="https://github.com/grails/grails-core/blob/master/grails-core/src/main/groovy/grails/util/GrailsUtil.java">GrailsUtil source code</a> shows that I could also suppress those warnings by passing <span style="font-family: 'Courier New', Courier, monospace;">grails.log.deprecated=false</span> into the JVM as a system property. I'll opt for the latter solution because my company has many Grails projects that share a common set of ant targets. Suppressing these spurious start-up warnings in general seems to me like a better default. The trade-off is that if any application code directly calls one of the deprecated APIs we won't get a helpful warning. I think that's a lesser risk than polluting our start-up logs with noise that encourages developers to ignore real problems. </div>
<div>
<br /></div>
Solution:<br />
Add <span style="font-family: 'Courier New', Courier, monospace;">-Dgrails.log.deprecated=false</span> to the GRAILS_OPTS environment variable in all IntelliJ configurations and to the ant file shared by most of the company's Grails apps.<br />
<div>
<br /></div>
<div>
<span style="font-size: large;">Useless log warnings about ehcache</span><br />
<br />
The start up log now contains new useless warnings.<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">2012-02-06 16:30:03,243 [Thread-10] WARN hibernate.AbstractEhcacheRegionFactory - Couldn't find a specific ehcache configuration for cache named [org.hibernate.cache.UpdateTimestampsCache]; using defaults.</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">2012-02-06 16:30:03,261 [Thread-10] WARN hibernate.AbstractEhcacheRegionFactory - Couldn't find a specific ehcache configuration for cache named [org.hibernate.cache.StandardQueryCache]; using defaults.</span><br />
<br />
Asgard doesn't use ehcache, domain objects, or hibernate. It mainly interacts with Amazon Web Services APIs rather than any traditional database. These crufty warnings are particularly meaningless and mysterious for me. They should not occur by default.<br />
<br />
Solution:<br />
Add the following to the log4j section of Config.groovy.<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">// Suppress otherwise unavoidable warnings in Grails start up logs</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">error 'net.sf.ehcache.hibernate.AbstractEhcacheRegionFactory'</span></div>
<div>
<br /></div>
<div>
<span style="font-size: large;">Action chain usage throws NullPointerException</span></div>
<br />
A NullPointerException gets thrown by hard-to-identify plumbing code when calling this method for a validation failure in a controller.<br />
<span style="font-family: 'Courier New', Courier, monospace;">chain(action: create, model: [cmd: cmd], params: params)</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span><br />
Result:<br />
<span style="font-family: 'Courier New', Courier, monospace;"></span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| Error 2012-01-30 14:02:01,406 ["http-bio-8080"-exec-4] ERROR errors.GrailsExceptionResolver - NullPointerException occurred when processing request: [POST] /application/save - parameters:</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">_action_save: </span><br />
<span style="font-family: 'Courier New', Courier, monospace;">cmc: </span><br />
<span style="font-family: 'Courier New', Courier, monospace;">alertingServiceKey: </span><br />
<span style="font-family: 'Courier New', Courier, monospace;">monitorBucketType: application</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">description: Testing nac</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">name: hellojsondow</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">owner: jsondow</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">type: Web Service</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Stacktrace follows:</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">Message: null</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> Line | Method</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">->> 161 | doCall in com.netflix.nac.ApplicationController$_closure6</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - </span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| 886 | runTask in java.util.concurrent.ThreadPoolExecutor$Worker</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">| 908 | run . . in ''</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">^ 680 | run in java.lang.Thread</span><br />
<br />
Printing out the values in the chain map shows that none of the values are null.<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">println create</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">println([cmd: cmd])</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">println params</span><br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">com.netflix.nac.ApplicationController$_closure5@5b736d8</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[cmd:com.netflix.nac.ApplicationCreateCommand@3e420e73]</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">[_action_save:, cmc:, alertingServiceKey:, monitorBucketType:application, description:Testing nac, name:hellojsondow, owner:jsondow, type:Web Service, action:save, controller:application]</span><br />
<br />
Something is going wrong under the hood in the chain method, which has never had a problem before. Googling leads to the <a href="http://grails.org/doc/2.0.x/ref/Controllers/chain.html">Grails chain documentation</a> which does not illuminate the error, but instead indicates that action is still supposed to be what it was in Grails 1.3.7: "The action to redirect to, either a string name or a reference to an action within the current controller." More googling leads to irrelevant-looking NullPointerExceptions. Possibly similar is <a href="http://grails.1312388.n4.nabble.com/Help-w-Exception-td4273937.html">this report</a> but there is no chain call there.<br />
<br />
For a few hours this one seemed like it could be a deal breaker. It defies all attempts at traditional debugging. I resorted to stabbing wildly in the dark by ignoring the documentation and my own better judgement and trying a string to identify the action instead of a direct reference to the action closure variable. This fixed the problem, although it's inferior code, since a string will not be checked by my IDE until runtime, so it is likely to take longer to discover a typo.<br />
<br />
While trying to debug through trial and error, it became clear that hot code replacement of controller code to the running server through IntelliJ no longer works reliably in Grails 2.0.0 on my machine. It just fails silently. Each experimental code change in IntelliJ requires a server restart. This eradicates the most significant of Grails' productivity wins for an existing application.<br />
<br />
Solution:<br />
Wrap all action values in single quotes. Despite what the <a href="http://grails.org/doc/2.0.x/ref/Controllers/chain.html">Grails 2.0.0 documentation says for chain</a>, the action value must be a String, not a Closure.<br />
<br />
<span style="font-size: large;">Hot code replacement is broken in IntelliJ for Grails 2.0.0</span><br />
<br />
Hot code replacement of a controller class from IntelliJ 10 no longer works. This may be the biggest problem of all. The loss of hot code replacement from my IDE would be such a large step backwards in productivity that I would need to start looking for an alternative to Grails that can do hot code replacement for most classes in IntelliJ. <br />
<br />
Googling the problem reveals these discussions and temporary solutions. It's a problem with Grails 2.0.0 running in IntelliJ 10 <b>and</b> IntelliJ 11.<br />
<a href="http://grails.1312388.n4.nabble.com/Grails-2-0-RC1-Auto-Reloading-td4023792.html">http://grails.1312388.n4.nabble.com/Grails-2-0-RC1-Auto-Reloading-td4023792.html</a><br />
<a href="http://dattein.com/blog/intellij-not-hot-deploying-grails-application/">http://dattein.com/blog/intellij-not-hot-deploying-grails-application/</a><br />
It's a pretty unfortunate workaround but it gets the job done if I really want Grails 2.0.0 and IntelliJ right away.<br />
<br />
Solution:<br />
Add this long magic spell to the VM parameters field of all IntelliJ configurations:<br />
-server -javaagent:/Users/jsondow/w1/Tools/groovy/grails-2.0.0/lib/com.springsource.springloaded/springloaded-core/jars/springloaded-core-1.0.2.jar -noverify -Dspringloaded=profile=grails<br />
<br />
<span style="font-size: large;">Native instrumentation error during start up</span><br />
<br />
Now that I'm using the javaagent parameter to enable springloaded my start up logs contain about 5 to 15 lines like these:<br />
<br />
<span style="font-family: 'Courier New', Courier, monospace;">*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at ../../../src/share/instrument/JPLISAgent.c line: 806</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at ../../../src/share/instrument/JPLISAgent.c line: 806</span><br />
<br />
This didn't happen in early experiments. I tried a thorough clean and rebuild on the command line but it's still happening on my machine. Googling it shows various old Java complaints about other frameworks.<br />
<br />
If I remove the springloaded jar parameters shown above then this problem goes away, but then I can't iterate on my code using a running server anymore, rendering Grails somewhat useless for development.<br />
<br />
Solution:<br />
None found.<br />
<br />
<span style="font-size: large;">The command line and VM parameters for Grails 2.0.0 are much more numerous and complicated than for Grails 1.3.7</span><br />
<br />
Solving some of the problems above introduces a new problem. Configuration management just got a lot more error prone.<br />
<br />
My old most common IntelliJ configuration was the following. It includes a couple of optional Asgard flags I made up to reduce start up time.<br />
Command line: run-app<br />
VM parameters: -XX:MaxPermSize=128m -Dserver.port=8080 -DskipCacheFill=true -DonlyRegions=us-east-1,eu-west-1<br />
<br />
To solve problems in Grails 2.0.0 that configuration needs to become the following in order to do the same thing.<br />
Command line: run-app <b>--non-interactive</b><br />
VM parameters: <b>-server -javaagent:/Users/jsondow/w1/Tools/groovy/grails-2.0.0/lib/com.springsource.springloaded/springloaded-core/jars/springloaded-core-1.0.2.jar -noverify -Dspringloaded=profile=grails -Dgrails.log.deprecated=false</b> -XX:MaxPermSize=128m -Dserver.port=8080 -DskipCacheFill=true -DonlyRegions=us-east-1,eu-west-1<br />
<br />
The reloading workaround requires a unique path to the springloaded-core jar for every developer. We have several full time and several part time developers on the project, with plans to open source the code to hundreds of develoeprs. The need for so much start up configuration just for the framework is a step further away from the ease of use that Grails 1.3.7 provided.<br />
<br />
Solution:<br />
None found.<br />
<br />
<span style="font-size: x-large;">Not upgrading yet</span><br />
<br />
My team agreed it was worth the exploration phase to document the problems I found so far. This will be a starter reference for us when Grails 2.0.1 or Grails 2.0.2 get released so I can try again to have a more positive upgrade experience. The mixin bug, the IntelliJ reload bug, the action closure bug, and the reliance on a snapshot version of the spock plugin are enough reasons for this upgrade to be more of a loss than a win for me. I'll try again with a future version of Grails.<br />
<br />
Even if the bugs were fixed in Grails/IntelliJ there are still some design decisions with which I disagree. The need for a --non-interactive flag seems to me like a poor decision. The interactive mode in Grails 1.3.7 existed but was opt-in. Making it opt-out makes it seem like Grails is trying to cater mostly to new developers working on the command line, with less regard for long-time Grails developers working on mature applications using an IDE and automated Jenkins builds. The repetitive logs in a Jenkins build or an IDE build reinforce this feeling. Animated dots to show the passage of time for new developers on the command-line are not a good enough reason to muddy up the start up logs for other developers.<br />
<div>
<br /></div>
There are several tens of thousands of issues in the Grails Jira backlog. It's a bit challenging to identify which Jira tickets may relate to any of the issues I've encountered. If I find or create relevant Jira tickets I'll link to them from my blog.<br />
<br />
Here's someone else's story about why large existing Grails apps might not want to upgrade to Grails 2.0.0: <a href="http://schneide.wordpress.com/2012/01/23/upgrading-your-app-to-grails-2-0-0-better-wait-for-2-0-1/">http://schneide.wordpress.com/2012/01/23/upgrading-your-app-to-grails-2-0-0-better-wait-for-2-0-1/</a><br />
<br />
Please tell me why I'm wrong on Twitter <a href="https://twitter.com/#!/joesondow">@joesondow</a> or in the comments below.<br />
<br />Joe Sondowhttp://www.blogger.com/profile/09328659376615417083noreply@blogger.com3tag:blogger.com,1999:blog-129109713061448315.post-74354362245300419492010-10-11T02:19:00.001-04:002010-12-04T14:11:05.171-05:00Silicon Valley Code Camp 2010Back in May 2010 I moved from <a href="http://www.iloveny.com/">New York</a> to <a href="http://www.siliconvalley.com/">Silicon Valley</a> to be among the densest population of software developers on Earth. There might be denser one in <a href="http://www.glyphweb.com/esky/stars/alphacentauri.html">Alpha Centauri</a> but their light signals haven't reached our telescopes. Yet.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEherJeJRdIkXL04MVzXhlpslrU1JkK0Wbi72VHavCMdRtVPM4DQWzrRlZI6ERVH665BXzkmJud3qm80vNIqgfIDUI-PfKSejkvY07oCEKkGMqsFjIqfvm7JwgTMpEG5_sk3m0uJarCejes/s1600/IMG_0165.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEherJeJRdIkXL04MVzXhlpslrU1JkK0Wbi72VHavCMdRtVPM4DQWzrRlZI6ERVH665BXzkmJud3qm80vNIqgfIDUI-PfKSejkvY07oCEKkGMqsFjIqfvm7JwgTMpEG5_sk3m0uJarCejes/s320/IMG_0165.JPG" width="320" /></a></div>
<br />
One of the main reasons I wanted to be in California was for the vibrant tech community. They put on some great conferences with amazing frequency. This weekend was a massive free developer conference called <a href="http://www.siliconvalley-codecamp.com/">Silicon Valley Code Camp</a>.<br />
<br />
Several thousand developers signed up for SVCC and almost half of them showed up at Foothill College in Los Altos Hills. I ran into a few friends and I met some new people. One thing that really stood out was the unusually high percentage of women at this developer conference.<br />
<br />
There were some good talks I went to on developing and testing HTML5 and jQuery, designing user experiences, coding in Scala, and continuous deployment.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhg_JR5xjByYd6Vxz9Ig4DjMmwckKZXAK9XuRZd8uuDkSFFbo6sE59BhUigItpYrsuszJSJ5JfKvobGvjkA6_uMueHE12FZabhQGvpjlxen8AomeiZ3uyxZm92Epdfk3-kAgdhZpk296O4/s1600/IMG_0211.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhg_JR5xjByYd6Vxz9Ig4DjMmwckKZXAK9XuRZd8uuDkSFFbo6sE59BhUigItpYrsuszJSJ5JfKvobGvjkA6_uMueHE12FZabhQGvpjlxen8AomeiZ3uyxZm92Epdfk3-kAgdhZpk296O4/s320/IMG_0211.JPG" width="320" /></a></div>
<br />
That's right, continuous deployment. Not continuous integration. <a href="http://eng.kaching.com/2010/05/deployment-infrastructure-for.html">Continuous deployment</a>. Love. It's an awesome practice that <a href="http://rosien.net/">Adam Rosien</a> and <a href="http://www.eishay.com/">Eishay Smith</a> at <a href="https://www.kaching.com/">kaChing</a> are following with gusto. Adam admitted they hadn't yet solved the problem of good front-end code coverage with Selenium, mostly because they hadn't yet gotten their Selenium builds sufficiently parallelized. Been there. I suspect they'll end up using Test Swarm or Sauce OnDemand when they get around to it.<br />
<br />
I'm getting increasingly interested in <a href="http://ejohn.org/blog/test-swarm-alpha-open/">Test Swarm</a> and <a href="http://docs.jquery.com/Qunit">QUnit</a>, since they sound like they maybe, possibly, hopefully, kind of might be able to do what <a href="http://seleniumhq.org/">Selenium</a> cannot yet do, which is run quickly and reliably and with minimal maintenance. Don't get me wrong, I have a huge crush on Selenium, but it's very high maintenance and it sometimes forgets my birthday. I can't help but notice QUnit and Test Swarm. After <a href="http://www.javaclimber.com/">Kevin Nilson</a>'s talk it sounds like all three might work together well.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihnUHlIgQ0FZyBksrH2sz0hlCCrN0bxcC3xEp4eE25e8Ol8bg5cO3Lv5U_KyhyhtCANd2YfnIAdn2cWa-TbYrSgJc2Lxzn7LwRwfCpcIOZmfHlswI-zLMonq9bVvh9xh8ZygQyvwQwMWU/s1600/IMG_0282.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihnUHlIgQ0FZyBksrH2sz0hlCCrN0bxcC3xEp4eE25e8Ol8bg5cO3Lv5U_KyhyhtCANd2YfnIAdn2cWa-TbYrSgJc2Lxzn7LwRwfCpcIOZmfHlswI-zLMonq9bVvh9xh8ZygQyvwQwMWU/s320/IMG_0282.JPG" width="320" /></a></div>
<br />
However, what I'm immediately jazzed to start using is the <a href="http://api.jquery.com/category/plugins/templates/">jQuery Templates</a> plugin. I knew it was coming, based on the buzz on the <a href="http://podcast.jquery.com/">The Official jQuery Podcast</a> and <a href="http://yayquery.com/">yayQuery Podcast</a> but I didn't realize (a) it's already released, (b) how awesome it is, (c) it was one of Microsoft's contributions. Hey, I'm a regular Java developer who knows Microsoft's reputation, but they did invent Ajax. They're not all bad, especially these days. Microsoft is like Xena and Angel. They used to be the big bad, but they've started to see the light. Will they save us when Google becomes evil? But I digress. Doris Chen showed off some new jQuery features, whetting my appetite to really ajaxify the web app I'm in charge of at Netflix. Doris was an <a href="http://www.java.net/pub/au/206">evangelist at Sun</a> before she became an <a href="http://blogs.msdn.com/b/dorischen/">evangelist at Microsoft</a>. This fact caused the audience to make scandalous noises like a shocking soap opera scene was unfolding.<br />
<br />
I'll talk about Netflix another time. Or <a href="http://twitter.com/joesondow">follow me on Twitter</a> if you're curious. I don't blog much. I mostly tweet.<br />
<br />
My good friend <a href="http://briccetti.blogspot.com/">Dave Briccetti</a> did a talk where he showed the code for his Scala Lift app <a href="http://github.com/dcbriccetti/bird-show">BirdShow</a> that he made for his <a href="http://www.briccettiphoto.com/">mom's web site</a> of gorgeous wildlife photos. This was one of the better Scala talks I've seen, because it focused on clean, practical usages of Scala instead of showing the most advanced features that Scala newbies can barely understand. After Dave's talk I got to hang around with him and work on some JavaScript.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPk38wNzZhmDgUwF1nsw1aLvZHn6__GtqqAW7TKzeSE0xNzULAA-v_jOLvAhVDi9yodnRRvESEgce9iEwLd-76eg8rkuzoCQ0c79nBZyrrg0qyfJW9tusX1J7v6q8WO-vZk11SZC0kDao/s1600/IMG_0182.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPk38wNzZhmDgUwF1nsw1aLvZHn6__GtqqAW7TKzeSE0xNzULAA-v_jOLvAhVDi9yodnRRvESEgce9iEwLd-76eg8rkuzoCQ0c79nBZyrrg0qyfJW9tusX1J7v6q8WO-vZk11SZC0kDao/s320/IMG_0182.JPG" width="320" /></a></div>
<br />
<a href="http://community.devexpress.com/blogs/markmiller/default.aspx">Mark Miller</a> gave a rousing talk on The Science of Great UI. I know some immediate changes I'll be making at work based on Mark's advice and examples. For instance, tables look better in Excel than Word because the lines are less important than the data so they should be lighter. I'm glad I took a lot of photos of slides, since the slides are not so easy to find for most speakers.<br />
<br />
What was your favorite tech conference?Joe Sondowhttp://www.blogger.com/profile/09328659376615417083noreply@blogger.com0tag:blogger.com,1999:blog-129109713061448315.post-90122585775174947562009-10-04T22:39:00.021-04:002009-10-05T13:21:35.544-04:00Fancy Hudson EmailOver lunch I was discussing email overload with <a href="http://ny-code-monkey.blogspot.com/">my coworker Scott</a>. I already use automatic color-coding and move-to-folder rules to help me find the important stuff at a glance. Still there are some automated emails that I must read, but which are time-consuming to deal with. <br />
<br />
One such email is the Hudson build result. <a href="https://hudson.dev.java.net/">Hudson</a> is a fantastic tool for continuous integration, but its email format leaves a lot to be desired: <br />
<br />
<div class="separator" style="clear: both;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-3DUm3mUa-kCDlXlWOE_1rJ68aGKS33jn2soikY_1x9IN9MSJBujNMu4kxz8adHmFosAUJbI_kOArdrIa5zpVLpYhR4mLeL4syJIi4_lA5ikFh7RAtEZ1UWpjPMYJCkfvMN2rKTj6lFA/s1600-h/hudson-email-before.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-3DUm3mUa-kCDlXlWOE_1rJ68aGKS33jn2soikY_1x9IN9MSJBujNMu4kxz8adHmFosAUJbI_kOArdrIa5zpVLpYhR4mLeL4syJIi4_lA5ikFh7RAtEZ1UWpjPMYJCkfvMN2rKTj6lFA/s400/hudson-email-before.png" /></a><br /></div>
<br />
From there I have to click the link, wait for a slow loading Hudson page, then click another link to view the full test results. Then I have to sift through the tests that only failed once because of environmental quirks and <a href="http://seleniumhq.org/projects/">Selenium</a> bugs, to identify the recurring test failures that need prompt attention. <br />
<br />
What I really want is something like this:<br />
<br />
<div class="separator" style="clear: both;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiapVL0NzAjqtuHGV8C8ksj-BeTQPgXPVw2yUL3iFLRnq-qWqFM_NgLsn7CDLUkKnLoUL0Ye1mtGSn9kJg8pzdHbntiyEd5HjkGe7bsN8YlHlQMKKIM-VS8eYDvGP-ZVP_35gb4J0Ic6mw/s1600-h/hudson-email-after.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiapVL0NzAjqtuHGV8C8ksj-BeTQPgXPVw2yUL3iFLRnq-qWqFM_NgLsn7CDLUkKnLoUL0Ye1mtGSn9kJg8pzdHbntiyEd5HjkGe7bsN8YlHlQMKKIM-VS8eYDvGP-ZVP_35gb4J0Ic6mw/s400/hudson-email-after.png" /></a><br /></div>
<br />
The subject line tells how many tests have failed. The recurring test failures are listed with direct links to each failure message. If there are no recurring failures then the test is marked a success. The duration is red for tests that took over a minute. If the test name and error message are short enough, the error message is displayed.<br />
<br />
Fortunately Scott had read a blog post with a solution that might allow us to write our own email output in Groovy. I googled it and found this gem by Chetan:
<a href="http://techkriti.wordpress.com/2008/08/30/using-groovy-with-hudson-to-send-rich-text-email/">Using Groovy with Hudson to send rich text email</a><br />
<br />
If you read Chetan's post and the comments, most problems come from trying to show recent SCM changes in Perforce, SVN, or CVS. That's not my top priority, so I'm starting with the output that I described above. Here's how I got it working:<br />
<ol>
<li>Download <a href="http://go2.wordpress.com/?id=725X1342&site=techkriti.wordpress.com&url=http%3A%2F%2Fchetan.mehrotra.googlepages.com%2Femail-ext.hpi">Chetan's enhanced email-ext plugin</a> for Hudson and add it to your Hudson setup at Hudson -> Manage Hudson -> Manage Plugins -> Advanced</li>
<li><a href="http://wiki.hudson-ci.org/display/HUDSON/Tomcat">Upgrade Hudson</a> to the latest version (1.326 when I started the project 2 days ago, although 1.327 came out last night, because <a href="http://www.kohsuke.org/" rel="nofollow">Kohsuke Kawaguchi</a> has superpowers and releases enhancements all the time)</li>
<li>Disable "E-mail Notification" and enable "Editable Email Notification"</li>
<li>Enable "Default Content is Script" and "Default Content Type is HTML"</li>
<li>Set the default subject to this Groovy template:<br />
<br />
<br />
<br />
<pre style="margin-left: -40px;">$DEFAULT_SUBJECT <% def tr = build.testResultAction; if (tr?.failCount) { %>(${tr?.failCount} failures ${tr?.failureDiffString}) <% } %></pre>
<br />
<br />
<br />
</li>
<li>Set the default content to this Groovy template, which you'll probably want to read and edit to suit your needs:</li>
</ol>
<pre><style>
body, table, td, th, p { font-family: Verdana,Helvetica,sans serif; font-size: 11px; }
.pane { margin-top: 4px; white-space: nowrap; }
table.pane { border: 1px solid #BBB; border-collapse: collapse; }
td.pane { border: 1px solid #BBB; padding: 3px 4px; vertical-align: middle; }
th { border: 1px solid #BBB; background-color: #F0F0F0; font-weight: bold; padding: 4px; }
</style>
<%
def stillFailing = []
def rootUrl = hudson.model.Hudson.instance.rootUrl
def jobName = build.parent.name
def buildNumber = build.number
def buildUrl = "${rootUrl}job/$jobName/$buildNumber/testReport/"
if (build.testResultAction) {
build.testResultAction.failedTests.each{tr ->
def packageName = tr.packageName
def simpleClassName = tr.simpleName
def testName = tr.safeName
def displayName = tr.className+"."+testName
def duration = tr.durationString;
if (duration.contains(" min")) {
duration = """<font color="red">""" + duration + "</font>"
}
def url = "${rootUrl}job/$jobName/$buildNumber/testReport/$packageName/$simpleClassName/$testName"
def error = (tr.errorDetails && tr.errorDetails.length() < 30 && displayName.length() < 100) ? tr.errorDetails : ""
error = error.replaceAll("<", "&lt;")
def failMap = [displayName:displayName,url:url,age:tr.age,error:error,duration:duration]
if (tr.age > 1) {
stillFailing << failMap
}
}
stillFailing = stillFailing.sort {it.displayName}
}
def emailHeader = stillFailing.size() > 0 ? "Recurring Failed Tests" : "Success"
%>
$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS:<br/>
<br/>
Check <a href="${buildUrl}">${buildUrl}</a> to view the results.<br/>
<h2>${emailHeader}</h2>
<table class="pane">
<tr>
<th>Test Name</th>
<th>Duration</th>
<th>Age</th>
</tr>
<% stillFailing.each { failedTest-> %>
<tr>
<td class="pane"><a href="${failedTest.url}">${failedTest.displayName}</a>&nbsp;&nbsp;${failedTest.error}</td>
<td class="pane" style="text-align: right;">${failedTest.duration}</td>
<td class="pane" style="text-align: right;">${failedTest.age}</td>
</tr>
<% } %>
</table>
</pre>
<br />
Chetan has submitted a <a href="https://hudson.dev.java.net/issues/show_bug.cgi?id=2175">patch for Hudson</a> to have this ability without hacking the email-ext plugin. Maybe someday this will be even more trivial to set up.<br />
<br />
It's easier to edit the Groovy code in the Hudson configure page textarea if that textarea is wider and uses a monospace font. To that end, I've written a Greasemonkey script called <a href="http://userscripts.org/scripts/show/59163">monospace-hudson</a> that makes those changes when the configure page loads. <br />
<br />
<div style="float: left;">
<h3>Without monospace-hudson</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLkew3qfkKYK8H-PXN1Bt0s9xIP8amrPxlZHCqIrfPlOL75gza2S5k-dmR3eAvx6R6nhyphenhyphen1WVdOMmiEYYn-TA4SNvPdlI31gt35DhvPdWW5D8yQDONorCBbYg2befJfUxJReelDo3edw6E/s1600-h/hudson-variable-space.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLkew3qfkKYK8H-PXN1Bt0s9xIP8amrPxlZHCqIrfPlOL75gza2S5k-dmR3eAvx6R6nhyphenhyphen1WVdOMmiEYYn-TA4SNvPdlI31gt35DhvPdWW5D8yQDONorCBbYg2befJfUxJReelDo3edw6E/s400/hudson-variable-space.png" /></a><br /></div>
</div>
<div style="float: left;">
<h3>With monospace-hudson</h3>
<div class="separator">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBCkNd5f6ATfAQJOZBuqHUv7gv4wAoUWU71dTzpBc3jo0_BrSxP5Cj0a9Ns8vmANuWMq4tOSe_5s3AHv7vm_ZmZ2U-4vuvIPn9yEd710GTcdBHJoWdPgezhsy3kG3yaPNJPsCkLpohnFY/s1600-h/hudson-monospace.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBCkNd5f6ATfAQJOZBuqHUv7gv4wAoUWU71dTzpBc3jo0_BrSxP5Cj0a9Ns8vmANuWMq4tOSe_5s3AHv7vm_ZmZ2U-4vuvIPn9yEd710GTcdBHJoWdPgezhsy3kG3yaPNJPsCkLpohnFY/s400/hudson-monospace.png" /></a><br /></div>
</div>
<br />
<div class="separator" style="clear: both;"></div>
<br />Joe Sondowhttp://www.blogger.com/profile/09328659376615417083noreply@blogger.com2tag:blogger.com,1999:blog-129109713061448315.post-49975085354177572672009-03-17T22:37:00.000-04:002009-03-17T22:49:00.869-04:00Presenting Open Spaces and Lightning TalksLast week I was getting over a cold at work when the CEO came to talk to me. For the company meeting she wanted employees to present on the conferences they had attended recently. I was still feeling a little under the weather, but I always enjoy telling people about the <a href="http://www.mindviewinc.com/Conferences/JavaPosseRoundup/">Java Posse Roundup</a> and the ideas and experiences I was exposed to there. So I gave her a synopsis about Open Space conferences and lightning talks. She liked the ideas and asked me to present them to the company. I agreed.<br /><br />The meeting began and soon moved on to the employee presentations. I was tired and weak from being sick the day before, but I wanted to communicate the Open Space ideas. I didn't know how many presentations there would be or when mine would be. The first few presentations went by, with detailed slides and lots of information about online advertising trends and healthcare marketing topics. I got increasingly nervous, since I had no slides and no written outline, and I felt more like going home and napping than improvising for a large audience.<br /><br />Finally the CEO introduced my talk at the end. Relieved to stop thinking and start talking, I began following <a href="http://agileartisans.com/">Jared Richardson's</a> advice from his <a href="http://career20.blogspot.com/">Career 2.0</a> talk at <a href="http://www.nofluffjuststuff.com/">No Fluff Just Stuff</a>.<br /><br /> • For each point you make, look a different audience member in the eye.<br /> • Pace slowly back and forth across the stage area, to keep people's eyes moving and their attention focused.<br /> • Use big arm gestures.<br /> • Tell jokes.<br /> • Modulate your voice high and low at different times.<br /><br />I explained how an Open Space conference works, how they've been covered by <a href="http://www.businessweek.com/magazine/content/07_20/b4034080.htm">Business Week</a> as a new way for conferences to educate like-minded people with an unconventional approach. I described the morning sessions at the Java Posse Roundup, where the attendees posted ideas for discussions on post-it notes and then met and recorded our discussions for the podcast. I talked about afternoons where we either went skiing and chatted about programming, or went to someone's house and did some coding to learn a new language. And then I talked about the evenings, where we did <a href="http://www.youtube.com/javaposse">lightning talks</a>.<br /><br />Fortunately it was a subject I'm already passionate about so it was easy to make it interesting. One of the ideas I wanted to sell was that a session of lightning talks should include a few talks that are included only to keep people amused and interested. The point is for their attention not to wander too far and to increase the audience's retention of the material. The inclusion of the "just for fun" talks has been slightly difficult for my teammates to swallow, so I wanted to address it specifically. As one coworker put it "If I have a deadline, why would I want to go in a conference room and listen to you talk about racecars?" As I explained to my audience, the point is to inspire creative thinking and self-expression, to get people practicing energetic public speaking, and to keep everyone awake and amused so they'll be more likely to retain the information from other lightning talks. And the most important part... if you don't like a presentation, it's only 5 minutes. Just wait for the next one.<br /><br />The audience laughed when I showed them the example of Andrew Harmel-Law's <a href="http://www.youtube.com/watch?v=rSgtBJhlVbA&feature=channel_page">"How to Prepare for Zombie Attacks"</a> lightning talk on YouTube. They applauded when I pointed out that they were laughing and would therefore probably remember some of the other points I made.<br /><br />The only question from the audience was "So how DO we prepare for zombie attacks?"Joe Sondowhttp://www.blogger.com/profile/09328659376615417083noreply@blogger.com0tag:blogger.com,1999:blog-129109713061448315.post-34720228167216266652009-03-11T19:47:00.000-04:002009-03-11T23:24:53.135-04:00SeleniumThe first of my 5-minute lightning talks has been posted to the <a href="http://www.youtube.com/javaposse">Java Posse YouTube channel</a>. Thanks to Carl Quinn for the videotaping and processing.<br /><br /><object width="480" height="295"><param name="movie" value="http://www.youtube.com/v/DFPacPVlUHk&hl=en&fs=1&rel=0"><param name="allowFullScreen" value="true"><param name="allowscriptaccess" value="always"><embed src="http://www.youtube.com/v/DFPacPVlUHk&hl=en&fs=1&rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="295"></embed></object><br /><br /><a href="http://www.youtube.com/watch?v=DFPacPVlUHk">Comment on this video</a><br /><br /><span style="font-size:130%;">Summary</span><br /><br /><a href="http://seleniumhq.org/">Selenium</a> is a web site testing tool that lets you build repeatable tests to ensure that all the important functionality of your web application still works after you've been altering your code base. The master copy of a test is saved as an HTML file, which can be edited easily in the Firefox plugin called the Selenium IDE. From there it can be saved as Java code to execute in a JUnit test, or in other languages like PHP, C#, Ruby, Perl, Python.<br /><br /><span style="font-size:130%;">The Good News</span><br /><br />Most interesting web applications depend on JavaScript, properly named hidden form inputs, and the integration of many systems working together in real time. The best way I've seen to test an entire system in a real browser is to create a collection of Selenium tests and run them automatically as a JUnit test suite. If you habitually write and maintain Selenium tests for all new and altered functionality on your web site, and run those tests a few times a day using a continuous integration system like <a href="https://hudson.dev.java.net/">Hudson</a>, you'll have a solid test bed that tells you as soon as someone on your team breaks something unexpectedly.<br /><br /><span style="font-size:130%;">The Bad News</span><br /><br />Selenium is young and finicky. It doesn't want to eat its brussels sprouts and it sometimes cries for a glass of water in the middle of the night. Because it runs in a real browser, it is subject to various meaningless error states that I've seen over the past 2 years of using it, including:<br /><ul><li>Random timeouts waiting for pages to load</li><li>Inability to use secure pop-up windows in IE</li><li>Overloaded CPU</li><li>Failure to start if Firefox is already running</li><li>A browser alert hangs the entire test suite indefinitely</li></ul><br /><span style="font-size:130%;">Conclusion</span><br /><br />Despite these monkey wrenches, I adore Selenium for its assurance that my web site works correctly before each release. It doesn't catch everything that a human QA team can find, but it does catch problems that a QA team doesn't have time to quadruple check every single week. You will probably need to write enough test utilities to restore the system to a known, deterministic state for each test. You'll also need to get your team in the habit of fixing a few tests for each significant code change, and learn to live with the fact that you won't always know who broke a test. Just fix it anyway. Once all that is in place, then you begin to have strong confidence that all your assumptions about your web site's functionality are still true six months later.Joe Sondowhttp://www.blogger.com/profile/09328659376615417083noreply@blogger.com2tag:blogger.com,1999:blog-129109713061448315.post-39052132344582756702009-03-08T23:26:00.000-04:002009-03-09T21:15:56.610-04:00Immediate Ideas from JPR09My 28 seconds of fame has arrived. If you're not already a Java Posse podcast listener, let episode 233 be your introduction to this excellent resource, and your chance to hear my radio voice as the <a href="http://www.mindviewinc.com/Conferences/JavaPosseRoundup/">Java Posse Roundup 2009</a> attendees all take a chance with the mic.<br /><br />I'm trying to recall the best ideas I learned at the roundup last week, for prompt use at my workplace, Marketing Technology Solutions. Some of the ideas about how to help get back developer time were interesting, especially the focus on automating more processes. I look forward to reviewing more of the audio sessions in future weeks and months to extract more ideas.<br /><br />However, there are three ideas that made my eyes light up when I heard them, and they're relatively simple to get started at work almost immediately after selling them to the team.<br /><br /><span style="font-size:130%;">1. Project Retrospectives</span><br /><br />After each code release, allow a full regular day to fix any surprising production issues. Then on the next day, run through a 1 to 3 hour code review and functionality retrospective. During this meeting each team member who completed anything interesting can show the primary changes they made to the code base and the improved functionality they created. This can help other team members learn how areas of the code are evolving that they haven't seen recently, and it can help us learn from each other's challenges and the solutions we found.<br /><br /><span style="font-size:130%;">2. Regular Discussions with Employees</span><br /><br />Spend 30-60 minutes each week with each employee to find out privately if there are any ideas or problems that deserve some attention from the employee's perspective. This should preferably be done far away from the office building to encourage unchecked speech about office troubles. It's reasonable to use a full or partial lunch break for the meeting if the employee wants, or just go for a walk during work hours.<br /><br /><span style="font-size:130%;">3. Lightning Talks</span><br /><br />Once a month our team can have an hour-long lunch-and-learn session of 5 minute lightning talks on any technical or non-technical subjects. Keeping things educational and entertaining helps promote free thinking and creativity, so non-technical talks are just as valuable as technical ones, as long as they're interesting or funny. This is especially helpful for team members who are not yet comfortable speaking in front of a group, which is an increasingly important skill for a developer. To get things started I'll need to prepare a few short talks on technical and non-technical subjects, with and without slides. This can help others see the flexibility of the format so they feel free to present whatever and however they prefer.<br /><br />The lightning talks at the Java Posse Roundup 2009 were very successful. They went as follows:<br /><br />TUESDAY<br /><span style="font-size:85%;">"Fair Allocation" Algorithm<br />Selenium Intro<br />How I Became 3/4 of the Man I Was<br />The Art of the Photo<br />High Gear Media and GWT<br />Loop Quantum Gravity<br />Animation Inside JavaFX<br />Racing 101<br />Awesome Productivity with GMail<br />ScalaCheck Automated Tests<br />Simple Twitter Client in Scala<br />Your Eyes Suck at Blue</span><br /><br />WEDNESDAY<br /><span style="font-size:85%;">Why Are There 12? or The Other Staircase<br />DB Migration in Java<br />Dynamic Web Skinning<br />Centerline Soccer<br />YQL<br />Java User Groups: How to Start One?<br />Sophisticated Data Access with JPA and Spring<br />F1 KERS System (2009)<br />Scala + Wicket<br />Surprise?<br />Slide Rules for Fun and Profit<br />Call 811<br />Repository Management</span><br /><br />THURSDAY<br /><span style="font-size:85%;">Fan<br />jFlubber, FlexFlubber, FXFlubber<br />The Smallest Plugin System<br />Hacking Hardware<br />Helmet Cam Footage<br />Zombies: Are You Prepared?<br />JavaScript Shell<br />Recovering a Stolen Laptop with Flex<br />Doctor Who<br />Sexier Software with Flex<br />Solar Power for Your House<br />Scala and JavaRebel<br />Groovy SwingBuilder, Google Maps, YQL Mashup<br /></span><span style="font-size:130%;"><br /></span>The JPR09 lightning talks are gradually getting released by Carl Quinn on <a href="http://www.youtube.com/javaposse">http://www.youtube.com/javaposse</a>Joe Sondowhttp://www.blogger.com/profile/09328659376615417083noreply@blogger.com2tag:blogger.com,1999:blog-129109713061448315.post-56676625182370022022009-03-07T21:33:00.001-05:002009-03-12T22:33:52.195-04:00Powers of Two<span style="font-size:130%;">When I was 2</span> my parents divorced, but I didn't know that at the time. I LOVED apple juice.<br /><br /><span style="font-size:130%;">When I was 4</span> I made an alphabet book with pop-up pictures of things that start with the 26 English letters.<br /><br /><span style="font-size:130%;">When I was 8</span> I learned to type by playing Infocom games. I had already decided that I would grow up to be a fantasy novelist or an Infocom game author. I was incorrect but I was starting down the right path.<br /><br /><span style="font-size:130%;">When I was 16</span> I designed theater sets and managed a crew to build them. I wrote and game-mastered year-long role playing games with my friends, which would later turn into an epic poem for high school credit. I didn't know it but all of this was gradually turning me into a software developer and manager.<br /><br /><span style="font-size:130%;">Today I turn 32</span> and I'm on a plane returning from the Java Posse Roundup in Crested Butte, Colorado. My driver's license vanished so the airport security folks find me interesting, and maybe even attractive. One guard was even so attentive that he wanted to pat down my arms and legs. He asked if I was sore or tender anywhere.<br /><br />I told him about falling on my ass repeatedly while cross-country skiing with computer geniuses. On the trail, Dianne Marsh patiently taught me the basics as we trudged through 3 miles of winding hills. The creator of Artifactory, Fred Simon, showed me how to cut the snow by bending my ankles correctly to go slowly down a hill.<br /><br />My brain is brimming with ideas from the conference. My housemate Todd Costella showed me the value of Crucible and Fisheye for recording code reviews. My other housemates Andrew Harmel-Law and D. J. Halberg helped me understand Scrum master training. The formal and informal discussions were eye-opening, and the 5-minute lightning talks were such a good idea I'm going to try to get them started at work.<br /><br />Dick Wall will gradually publish the audio of the discussions and the audio-video of the lightning talks. Thanks for everything, Dick! I'll be listening to those files many times, including the ones I was present for. Until the files are available I think I should blog about what I remember. This way I'll have a way to review my experiences and convert them into actions.<br /><br />Tor Norbye shares my appreciation for fruity cocktails instead of beer. I introduced him to the joy of the Bay Breeze (Malibu rum, vodka, cranberry, pineapple). I'll have to find out his favorite drink so I can try it out as well. He's already shown me how valuable Netbeans is for Groovy, JavaScript, JavaFX, and Matisse.<br /><br />On this same plane with me is Bill Robertson who redesigned the look and feel of Dick Wall's JFlubber application for the new and improved FlexFlubber and JFXFlubber incarnations. I sliced and diced Bill's design in Photoshop to make an exported JavaFX app. James Ward grabbed the PSD to make FlexFlubber. A few of the drop shadows didn't get into the buttons in Flex but it still looks wicked cool.<br /><br />Our wrap-up session from last night is already uploaded as episode #233 of the Java Posse Podcast at http://www.javaposse.com and the best of the lightning talks are starting to show up on http://www.youtube.com/javaposse<br /><span style="font-size:130%;"><br />When I'm 64</span> I could be handy mending a fuse when the lights have gone.Joe Sondowhttp://www.blogger.com/profile/09328659376615417083noreply@blogger.com3tag:blogger.com,1999:blog-129109713061448315.post-90680256432427360172008-11-24T22:30:00.000-05:002008-11-25T15:37:50.101-05:00How to add a CVS account<div>How to add a CVS account (example user jsmith):</div> <div>log in to the linux box<br />su - root<br />cd /home (to see list of users)<br />/usr/sbin/useradd jsmith<br />vi /etc/group<br />Find the cvs line and append ,jsmith<br />:wq (save the group file)<br />vi /etc/passwd<br />Find jsmith at the end of the file, change its second number to match other cvs users (group number)<br />:wq (save the passwd file)<br />/usr/bin/passwd jsmith<br />Enter new password</div><br />How to remove a CVS account (example user jsmith):<br />/usr/sbin/userdel jsmith<br />vi /etc/group<br />Delete jsmith line and ensure jsmith is removed from cvs line<br />:wq (save the group file)<br />vi /etc/passwd<br />Ensure jsmith line is deleted<br /><br /><br />Variation for Solaris:<br /><br />1. mkdir -p /export/home/jsmith (<i>Create the home directory for the user, before creating the user</i>)<br />2. useradd -s /bin/sh -d /export/home/jsmith jsmith (<i>Default shell will be sh. Home directory will be in /export/home</i>)<br />3. chown -R jsmith /export/home/jsmith (<i>Let the user own his home directory</i>)<br />4. passwd jsmith (<i>Change the password</i>)Joe Sondowhttp://www.blogger.com/profile/09328659376615417083noreply@blogger.com0tag:blogger.com,1999:blog-129109713061448315.post-83275032043615952622008-09-07T01:02:00.000-04:002008-09-07T01:06:11.643-04:00Favorite Quotes from Rich Web Experience"It's probably more work to do a careful evaluation than to write your own."<br />-- Douglas Crockford on which JavaScript library is the best to use<br /><br />"I still buy my kids presents on the day that Firebug came out."<br />-- Nik Krimm<br /><br />"Okay, that's it. Thank you for coming. Let's break for dinner. But first, a quick one hour demo..."<br />-- Stuart Halloway after his last slideJoe Sondowhttp://www.blogger.com/profile/09328659376615417083noreply@blogger.com0tag:blogger.com,1999:blog-129109713061448315.post-83689280026559301632008-09-06T17:45:00.000-04:002008-09-06T23:14:13.696-04:00Rich Web ExperienceThe <a href="http://www.therichwebexperience.com">Rich Web Experience</a> conference has come to an end. It ended with a bang as Jared Richardson presented an inspiring keynote encouraging the audience to write, speak, and teach in the tech community. As usual most of what he has to say is incredibly helpful, so here I go.<br /><br />The conference was great. It had slightly lower energy than <a href="http://www.nofluffjuststuff.com">No Fluff Just Stuff</a> in Princeton. I attribute this to the fact that we were mostly discussing very overlapping topics for 8-14 hours a day for three days which may slow down the brain a little. However, the material was excellent.<br /><br />Most of the speakers used slide decks that were better than the ones distributed from the logged in state of the <a href="http://www.therichwebexperience.com">Rich Web Experience</a> web site. I asked Jay Zimmerman when the updated slides would mostly be available, and he guesstimated Monday. I'm looking forward to performing my own pale imitations of a few of the sessions for the benefit of my coworkers at <a href="http://www.mtscorp.com">MTS</a>.Joe Sondowhttp://www.blogger.com/profile/09328659376615417083noreply@blogger.com0