<?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-15139516</id><updated>2012-01-26T09:49:52.001-05:00</updated><category term='linux'/><category term='RailsConf2007'/><category term='clustering'/><category term='drop-down menus'/><category term='sqlserver'/><category term='ruby on rails'/><category term='unix'/><category term='tomcat'/><category term='dst'/><category term='daylightSavings'/><category term='wirless'/><category term='apache httpd'/><category term='windows 7'/><title type='text'>bhansley</title><subtitle type='html'>Views of a Geek</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>20</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-15139516.post-3490279340128394661</id><published>2010-10-05T09:38:00.006-04:00</published><updated>2010-10-07T07:39:13.235-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='windows 7'/><category scheme='http://www.blogger.com/atom/ns#' term='wirless'/><title type='text'>Windows 7, Wired and Wireless networks</title><content type='html'>I've been on Windows 7 (64-bit) for a few months now, and just recently moved to a new Lenovo T510. The move, using Windows backup and restore, was downright painless and just worked.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;One issue I had with the new setup that was frustrating me was that, when docked, the laptop would keep a strong preference for wireless connections over wired ones. Apparently, Windows 7 defaults to using some customized route preference calculation metric, and had my wireless at 90 and my wired connection at 900-ish. (Lower scores indicate stronger affinity.) &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;You can see your current, auto-assigned routing metrics by typing &lt;span class="Apple-style-span"&gt;netstat -r&lt;/span&gt; from a command prompt, and looking in the 'Metric' column of hte IPv4 Route Table.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When I'm docked, I want wired to take precedent, as the wired connection has a static IP, and makes certain dev and admin tasks possible that cant' be done around here on a regularly-shifting DHCP address on wireless - things like sending SMTP mails without credentials, the way servers get to.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So, using an articles from &lt;a href="http://lifehacker.com/5632098/how-to-make-windows-choose-between-wired-and-wi+fi-networks"&gt;Lifehacker&lt;/a&gt; and  &lt;a href="http://blog.palehorse.net/2009/08/24/using-windows-7-with-multiple-gateways-and-dhcp/"&gt;Palehorse&lt;/a&gt; as a starting point, I set forth to change my interface metrics to suit my needs.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;Starting with Control Panel\Network and Internet\Network and Sharing Center, click the wired connection under Connections:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_-QlIh3nxW4c/TKsv3-nqDTI/AAAAAAAAAUc/LixOOUqqF5Y/s1600/Sharing.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 203px;" src="http://3.bp.blogspot.com/_-QlIh3nxW4c/TKsv3-nqDTI/AAAAAAAAAUc/LixOOUqqF5Y/s320/Sharing.PNG" border="0" alt="" id="BLOGGER_PHOTO_ID_5524562006648687922" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Then click 'Properties'.&lt;/li&gt;&lt;li&gt;Then select 'Internet Protocol Version 4 (TCP/IPv4)' and click 'Properties'.&lt;/li&gt;&lt;li&gt;Click 'Advanced' at the bottom of the window.&lt;/li&gt;&lt;li&gt;Un-check the 'Automatic Metric' box, and enter a new routing metric here. Lower numbers are higher priority.  I used a routing metric of 10 for my wired connection and 200 for my wireless, and everything seems to be behaving exactly as I want.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_-QlIh3nxW4c/TKswTRaKqDI/AAAAAAAAAUk/AjPoBI07pIo/s1600/Metric.PNG"&gt;&lt;img style="display: block; margin-top: 0px; margin-right: auto; margin-bottom: 10px; margin-left: auto; cursor: pointer; width: 223px; height: 93px; text-align: center; " src="http://1.bp.blogspot.com/_-QlIh3nxW4c/TKswTRaKqDI/AAAAAAAAAUk/AjPoBI07pIo/s320/Metric.PNG" border="0" alt="" id="BLOGGER_PHOTO_ID_5524562475548846130" /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Repeat from #1 selecting your wireless network connection.&lt;/li&gt;&lt;li&gt;Reboot when done to have the new assignments take effect.&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;As above, you can verify your assignments by typing &lt;span class="Apple-style-span"&gt;netstat -r&lt;/span&gt; from a command prompt, and looking in the 'Metric' column of hte IPv4 Route Table.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A word of warning: you may want to check with your networking people before you do this. Routers can assign this metric, and overriding what they assign may have unintended consequences for how you access your networks.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Bill&lt;/div&gt;&lt;div&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/15139516-3490279340128394661?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/3490279340128394661/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=3490279340128394661' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/3490279340128394661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/3490279340128394661'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2010/10/windows-7-wired-and-wireless-networks.html' title='Windows 7, Wired and Wireless networks'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_-QlIh3nxW4c/TKsv3-nqDTI/AAAAAAAAAUc/LixOOUqqF5Y/s72-c/Sharing.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-4387780784772216588</id><published>2010-05-13T13:42:00.006-04:00</published><updated>2012-01-22T08:29:00.233-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='daylightSavings'/><category scheme='http://www.blogger.com/atom/ns#' term='dst'/><category scheme='http://www.blogger.com/atom/ns#' term='sqlserver'/><title type='text'>Daylight Savings Time in SQL Server</title><content type='html'>We're running a vendor system that stores &lt;b&gt;every &lt;/b&gt;date/time object in GMT. This isn't a bad thing, but it does require converting GMT -&amp;gt; Local time in every place we used it: select statements, where clauses, corelated subqueries, etc. To do this we started out years ago with a simple Function plus a database table to store DST dates.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That function looked like this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;CREATE FUNCTION [dbo].[ConvertGMT]&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;(@MYDATE DATETIME)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;RETURNS DATETIME AS&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;BEGIN&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    DECLARE @OFFSET INT&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    DECLARE @YEAR INT&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    DECLARE @DST_BEGIN DATETIME&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    DECLARE @DST_END DATETIME&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    DECLARE @RETURNED_DATE DATETIME&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    SET @YEAR = YEAR(@MYDATE)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    SET @DST_BEGIN = (select DST_BEGIN from &lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;dbo.DST_LOOKUP where YEAR_LOOKUP = @YEAR)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    SET @DST_END = (select DST_END from &lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;dbo.DST_LOOKUP where YEAR_LOOKUP = @YEAR)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;   &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    IF (@MYDATE BETWEEN @DST_BEGIN AND @DST_END)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;        SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;        SET @OFFSET = -5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    SET @RETURNED_DATE = DATEADD(hour,@OFFSET,@MYDATE)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;RETURN @RETURNED_DATE&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;END&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;and the associated table looked like this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;/span&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;b&gt;DST_BEGIN&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;b&gt;DST_END&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;b&gt; &lt;/b&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;b&gt;  &lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;b&gt;YEAR_LOOKUP&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;4/4/1999 2:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;10/31/1999 3:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;1999&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;4/2/2000 2:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;10/29/2000 3:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;2000&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;4/1/2001 2:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;10/28/2001 3:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;2001&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;4/7/2002 2:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;10/27/2002 3:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;2002&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;4/6/2003 2:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;10/26/2003 3:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;2003&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;4/4/2004 2:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;10/31/2004 3:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;2004&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;4/3/2005 2:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;10/30/2005 3:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;2005&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;4/2/2006 2:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;10/29/2006 3:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;2006&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;3/11/2007 2:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;11/4/2007 3:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;2007&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;3/9/2008 2:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;11/2/2008 3:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;2008&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;3/8/2009 2:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;11/1/2009 3:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;2009&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;3/14/2010 2:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;11/7/2010 3:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;2010&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;3/13/2011 2:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;11/6/2011 3:00:00 AM&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;2011&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;This worked well enough for a while, but it cost a couple of table hits for every conversion. That adds up when you're doing 6 conversion per row returning a couple hundred rows a few times a minute. This benchmarked at &lt;b&gt;&amp;cong;15seconds&lt;/b&gt; to return 45,000 at 3 conversions / row in a simple select.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Our next step was to eliminate the table lookup. That was simple and inelegant, and looked like this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;ALTER FUNCTION [dbo].[ConvertGMT] (@MYDATE DATETIME)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;RETURNS DATETIME AS&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;BEGIN&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    DECLARE @OFFSET INT&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    DECLARE @YEAR INT&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    DECLARE @RETURNED_DATE DATETIME&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    SET @YEAR = YEAR(@MYDATE)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    IF @YEAR = 2010 AND @MYDATE between 'Mar 14 2010  2:00AM ' and 'Nov  7 2010  3:00AM ' SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 2009 AND @MYDATE between 'Mar  8 2009  2:00AM ' and 'Nov  1 2009  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 2008 AND @MYDATE between 'Mar  9 2008  2:00AM ' and 'Nov  2 2008  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 2007 AND @MYDATE between 'Mar 11 2007  2:00AM ' and 'Nov  4 2007  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 2006 AND @MYDATE between 'Apr  2 2006  2:00AM ' and 'Oct 29 2006  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 2005 AND @MYDATE between 'Apr  3 2005  2:00AM ' and 'Oct 30 2005  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 2004 AND @MYDATE between 'Apr  4 2004  2:00AM ' and 'Oct 31 2004  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 2003 AND @MYDATE between 'Apr  6 2003  2:00AM ' and 'Oct 26 2003  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 2002 AND @MYDATE between 'Apr  7 2002  2:00AM ' and 'Oct 27 2002  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 2001 AND @MYDATE between 'Apr  1 2001  2:00AM ' and 'Oct 28 2001  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 2000 AND @MYDATE between 'Apr  2 2000  2:00AM ' and 'Oct 29 2000  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 1999 AND @MYDATE between 'Apr  4 1999  2:00AM ' and 'Oct 31 1999  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 2011 AND @MYDATE between 'Mar 13 2011  2:00AM ' and 'Nov  6 2011  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 2012 AND @MYDATE between 'Mar 11 2012  2:00AM ' and 'Nov  4 2012  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 2013 AND @MYDATE between 'Mar 10 2013  2:00AM ' and 'Nov  3 2013  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE IF @YEAR = 2014 AND @MYDATE between 'Mar  9 2014  2:00AM ' and 'Nov  2 2014  3:00AM '  SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE SET @OFFSET = -5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    SET @RETURNED_DATE = DATEADD(hour,@OFFSET,@MYDATE)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;RETURN @RETURNED_DATE&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;END&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;The performance boost with this version was amazing. It processed the same 45,000 x 3 conversion / row query in &amp;cong;3 seconds. Across all our GUI and report calls, that was a noticeable performance boost. But, it looked like something someone in CS101 would write, and it still required occasional updates to add new years and keep current.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The final step - and current solution - started with &lt;a href="http://www.mssqltips.com/tip.asp?tip=1372"&gt;a DST discussion&lt;/a&gt; at &lt;a href="http://www.mssqltips.com/"&gt;MSSqlTips&lt;/a&gt; that used some pre-calculated offsets to dynamically calculate DST start and stop dates based on the DOW of the start and end months. This worked well for dates 2007 and later, but earlier dates had a different date pattern. I continued the same model that &lt;a href="http://www.mssqltips.com/author.asp?authorid=1"&gt;Tim Cullen&lt;/a&gt; started with the MSSqlTips post and used a set of precalculated offsets for 2006 and earlier starts, and dynamically calculated DST end dates. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The final function looks like this:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;CREATE FUNCTION [dbo].[ConvertGMT]&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;(@MYDATE DATETIME)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;RETURNS DATETIME AS&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;BEGIN&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    DECLARE @OFFSET INT&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    DECLARE @YEAR INT&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    DECLARE @DST_BEGIN DATETIME&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    DECLARE @DST_END DATETIME&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    DECLARE @RETURNED_DATE DATETIME&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;   SET @YEAR = YEAR(@MYDATE)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    declare @DSTStartWeek smalldatetime, @DSTEndWeek smalldatetime&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    if @YEAR &amp;gt;= 2007&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;        BEGIN&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            set @DSTStartWeek = '03/01/' + convert(varchar,@YEAR)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            SET @DST_BEGIN = case datepart(dw,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 1 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,170,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 2 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,314,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 3 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,290,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 4 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,266,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 5 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,242,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 6 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,218,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 7 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,194,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            end&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            set @DSTEndWeek = '11/01/' + convert(varchar,@Year)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            SET @DST_END = case datepart(dw,dateadd(week,1,@DSTEndWeek))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 1 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,2,@DSTEndWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 2 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,146,@DSTEndWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 3 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,122,@DSTEndWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 4 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,98,@DSTEndWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 5 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,74,@DSTEndWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 6 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,50,@DSTEndWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 7 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,26,@DSTEndWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            end&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;        END&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;        BEGIN&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            set @DSTStartWeek = '04/01/' + convert(varchar,@YEAR)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            SET @DST_BEGIN = case datepart(dw,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 1 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,2,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 2 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,146,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 3 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,122,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 4 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,98,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 5 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,74,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 6 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,50,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            when 7 then&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            dateadd(hour,26,@DSTStartWeek)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            end&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;                        SET @DSTEndWeek = '11/01/' + convert(varchar,@Year)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;            SET @DST_END = dateadd(hh,3,dateadd (dd,(1 - datepart(dw,@DSTEndWeek)),@DSTEndWeek))&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;          END&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    IF  @MYDATE between @DST_BEGIN and @DST_END&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;        SET @OFFSET = -4&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    ELSE &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;        SET @OFFSET = -5&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;    SET @RETURNED_DATE = DATEADD(hour,@OFFSET,@MYDATE)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-tab-span" style="white-space:pre"&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;RETURN @RETURNED_DATE&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family:'courier new';"&gt;&lt;span class="Apple-style-span" style="font-size:small;"&gt;END&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;This code has one change from Tim's code - it now ends at 3am in November, for 2007 and later. This is what we have in production now. It's just as performant as the previous version (&amp;cong;3 sec for the benchmark query), but it has the advantage of never needing updating for new years. It's more elegant than it's predecessors, but I have no doubt that it could be done better.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Use, share, comment.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;-Bill&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;(Thanks to the &lt;a href="http://en.wikipedia.org/wiki/Daylight_saving_time_in_the_United_States"&gt;Wikipedia DST Article&lt;/a&gt; for clarity on this.)&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-4387780784772216588?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/4387780784772216588/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=4387780784772216588' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/4387780784772216588'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/4387780784772216588'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2010/05/daylight-savings-time-in-sql-server.html' title='Daylight Savings Time in SQL Server'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-5261478602597023849</id><published>2009-06-26T14:04:00.003-04:00</published><updated>2009-06-28T23:24:34.190-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='apache httpd'/><category scheme='http://www.blogger.com/atom/ns#' term='clustering'/><category scheme='http://www.blogger.com/atom/ns#' term='tomcat'/><title type='text'>Apache, SSL and Tomcat Clustering</title><content type='html'>With help from the Apache HTTPd docs, the Apache Tomcat docs, and lots of Google-help, I've set up a failover-friendly load balanced server pair that seriously ups our game in terms of high availibility, and boosted performance as a by-product.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;Background&lt;/b&gt; (skip this if you just want to get to the server setup details)&lt;/div&gt;&lt;div&gt;My group does custom web applications for use the Operating Rooms. We interface with multiple clinical and administrative systems and show, collect, and share data that's critical to the operations and to patient safety. Our longest running application, ORview, is a Java web app that collects pre-operative assessments and post-operative assessments, provides airport monitor-style big screen views throughout the OR area, and provides other billing, reporting and QA/QI functionality. Our newest application is RequestOR, which collects posting requests for new case postings, and shares, via HL7, this data with GE's Centricity Periopertive Manager and IDX. Our old ORview prod server was at end-of-life, and we were needing a new server to deploy RequestOR on so we designed, spec'd and installed a new server cluster that'll meet the needs of both of our major applications, and give us some room to migrate some of our more minor applications (Java and JRuby).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Server Hardware&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Qty 2 - IBM HS21 Blade Server, Dual Xenon Quad-core @ 3.0GHz, 8GB PC2-5300 RAM, 146G SAS HDD. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Server Software&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Red Hat Enterprise Linux Server release 5.3 (Tikanga)&lt;/div&gt;&lt;div&gt;Java SE Runtime Environment 64-bit (build 1.6.0_13-b03)&lt;/div&gt;&lt;div&gt;Apache HTTPd 2.2.3 (httpd-2.2.3-22.el5_3.1) with mod_ssl (mod_ssl-2.2.3-22.el5_3.1) and mod_proxy_ajp (as part of the httpd install)&lt;/div&gt;&lt;div&gt;Apache Tomcat 6.0.18&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Network/DNS Configuration&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;The key to doing multiple SSL-secured applications is that each unique SSL certificate NEEDS it's own IP address and host name to bind to. (There are some budding ways around that but none of those were mature enough going into this process to be a viable production option.)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="text-decoration: underline;"&gt;requestor&lt;/span&gt;: 10.20.215.226, 10.20.215.228 - configured as dns round-robin&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="text-decoration: underline;"&gt;orview2&lt;/span&gt;: 10.20.215.227, 10.20.215.229 – configured as dns round-robin&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Those IPs get distrubuted to each server that's hosting that application, along with each server having their own ip.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="text-decoration: underline;"&gt;server 1&lt;/span&gt;&lt;/div&gt;&lt;div&gt;10.20.215.223 – blade1&lt;/div&gt;&lt;div&gt;10.20.215.226 – requestor &lt;/div&gt;&lt;div&gt;10.20.215.227 – orview2&lt;/div&gt;&lt;div&gt;228.0.0.23 - multicast&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="text-decoration: underline;"&gt;server 2&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10.20.215.224 – blade2&lt;/div&gt;&lt;div&gt;10.20.215.228 – requestor (dns rr)&lt;/div&gt;&lt;div&gt;10.20.215.229 – orview2 (dns rr)&lt;/div&gt;&lt;div&gt;228.0.0.23 - multicast&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Apache HTTPd Configuration&lt;/b&gt;&lt;/div&gt;&lt;div&gt;First, the explanation. There's a default port 80 host (in black) that just answers requests on the machine's unique IP. We use this index.html to point to a simple machine ident. The RequestOR section is next (in &lt;span class="Apple-style-span"  style="color:#6633FF;"&gt;blue&lt;/span&gt;). It defines a port 80 host that redirects all requests to the port 443 (ssl) version of itself. The second virtual host in the blue is the ssl version. This section contains config info for the SSL certificates, and a reference to balancer://ajpCluster/requestor as handler for all requests to this virtualhost. The ORview2 section (in &lt;span class="Apple-style-span"  style="color:#FF0000;"&gt;red&lt;/span&gt;) largely duplicates this configuration for the second ssl application on these servers. The second server's config files are identical, except the IPs are changed to match that server's configuration. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&amp;lt;VirtualHost *:80&amp;gt;&lt;br /&gt;DocumentRoot /var/www/html&lt;br /&gt;ServerName blade1.foo.edu&lt;br /&gt;&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;&lt;span class="Apple-style-span"  style="color:#6633FF;"&gt;# RequestOR virtual hosts&lt;br /&gt;&amp;lt;VirtualHost 10.20.215.226:80&amp;gt;&lt;br /&gt;DocumentRoot /var/www/html/requestor&lt;br /&gt;ServerName requestor.foo.edu&lt;br /&gt;Redirect permanent / https://requestor.foo.edu/&lt;br /&gt;&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;&amp;lt;VirtualHost 10.20.215.226:443&amp;gt;&lt;br /&gt;DocumentRoot /var/www/html/requestor&lt;br /&gt;ServerName requestor.foo.edu&lt;br /&gt;SSLCertificateFile&lt;br /&gt;/etc/pki/tls/certs/requestor.foo.edu.crt&lt;br /&gt;SSLCertificateKeyFile&lt;br /&gt;/etc/pki/tls/private/requestor.foo.edu.key&lt;br /&gt;SSLEngine on&lt;br /&gt;SSLProtocol all -SSLv2&lt;br /&gt;&amp;lt;Location /&amp;gt;&lt;br /&gt;ProxyPass balancer://ajpCluster/requestor&lt;br /&gt;stickysession=JSESSIONID&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;# ErrorLog logs/dummy-host.example.com-error_log&lt;br /&gt;# CustomLog logs/dummy-host.example.com-access_log common&lt;br /&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span"  style="color:#FF0000;"&gt;#ORview2 virtual hosts&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="color:#FF0000;"&gt;&amp;lt;VirtualHost 10.20.215.227:80&amp;gt;&lt;br /&gt;DocumentRoot /var/www/html&lt;br /&gt;ServerName orview2.foo.edu&lt;br /&gt;Redirect permanent / https://orview2.foo.edu/&lt;br /&gt;&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;&amp;lt;VirtualHost 10.20.215.227:443&amp;gt;&lt;br /&gt;DocumentRoot /var/www/html&lt;br /&gt;ServerName orview2.foo.edu&lt;br /&gt;SSLCertificateFile /etc/pki/tls/certs/orview2.foo.edu.crt&lt;br /&gt;SSLCertificateKeyFile&lt;br /&gt;/etc/pki/tls/private/orview2.foo.edu.key&lt;br /&gt;SSLEngine on&lt;br /&gt;SSLProtocol all -SSLv2&lt;br /&gt;&amp;lt;Location /&amp;gt;&lt;br /&gt;ProxyPass balancer://ajpCluster/orstat stickysession=JSESSIONID&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="color:#FF0000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold; "&gt;Apache mod_proxy_ajp Configuration&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;This is the config for the ajp load balancer. Tomcat on each server is set to listen for AJP requests on port 8009. This config file (the same on each server) tells the AJP balancer about the cluseter composed of TomcatA and TomcatB. In the absence of any other details, it'll default to sending new requests to the least loaded Tomcat server, and sending requests from existing sessions to the server that's been handling them. That's what the stickysessions attribute makes happen. Proxy listeners are configured for each application, same color scheme as above.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;LoadModule proxy_ajp_module modules/mod_proxy_ajp.so&lt;br /&gt;#&lt;br /&gt;# When loaded, the mod_proxy_ajp module adds support for&lt;br /&gt;# proxying to an AJP/1.3 backend server (such as Tomcat).&lt;br /&gt;# To proxy to an AJP backend, use the "ajp://" URI scheme;&lt;br /&gt;# Tomcat is configured to listen on port 8009 for AJP requests&lt;br /&gt;# by default.&lt;br /&gt;#&lt;br /&gt;&amp;lt;Location /balancer-manager&amp;gt;&lt;br /&gt;SetHandler balancer-manager&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&amp;lt;Proxy balancer://ajpCluster&amp;gt;&lt;br /&gt;BalancerMember ajp://blade1.foo.edu:8009 route=tomcatA&lt;br /&gt;BalancerMember ajp://blade2.foo.edu:8009 route=tomcatB&lt;br /&gt;&amp;lt;/Proxy&amp;gt;&lt;br /&gt;&lt;span class="Apple-style-span"  style="color:#3366FF;"&gt;&amp;lt;Location /requestor&amp;gt;&lt;br /&gt;ProxyPass balancer://ajpCluster/requestor stickysession=JSESSIONID&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span"  style="color:#FF0000;"&gt;&amp;lt;Location /orstat&amp;gt;&lt;br /&gt;ProxyPass balancer://ajpCluster/orstat stickysession=JSESSIONID&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="color:#FF0000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="color:#FF0000;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Tomcat's server.xml&lt;/b&gt;&lt;/div&gt;&lt;div&gt;The server.xml is generally a good sized file where most of the defaults are just fine. I've excerpted the relevant bits here that I had to change to get clustering working.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;A connector is defined in the &amp;lt;server&amp;gt; portion of the file. It should be enabled by default.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;  &amp;lt;!-- Define an AJP 1.3 Connector on port 8009 --&amp;gt;&lt;br /&gt;  &amp;lt;Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There should be one &amp;lt;engine&amp;gt; element. Edit it to include the unique name used for this in the proxy-ajp config file - tomcatA in this case. The other server's server.xml looks the same except for tomcatB here.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&amp;lt;Engine name="Catalina" defaultHost="localhost" jvmRoute="&lt;span class="Apple-style-span"  style="color:#FF0000;"&gt;&lt;b&gt;tomcatA&lt;/b&gt;&lt;/span&gt;"&amp;gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;A cluster element is defined inside the &amp;lt;engine&amp;gt; element.  The important thing to set here is the multicast address (in &lt;span class="Apple-style-span"  style="color:#FF0000;"&gt;red&lt;/span&gt;) to be used by Tribes to synchronize session information across the servers in the cluster. The FarmWarDeployer (in &lt;span class="Apple-style-span"  style="color:#3366FF;"&gt;blue&lt;/span&gt;) is experimental and (as of when this was written) isn't ready for prime-time.&lt;br /&gt;&lt;br /&gt;    &amp;lt;Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"&lt;br /&gt;               channelSendOptions="6"&amp;gt;&lt;br /&gt;&lt;br /&gt;        &amp;lt;Manager className="org.apache.catalina.ha.session.BackupManager"&lt;br /&gt;                 expireSessionsOnShutdown="false"&lt;br /&gt;                 notifyListenersOnReplication="true"&lt;br /&gt;                 mapSendOptions="6"/&amp;gt;&lt;br /&gt;        &amp;lt;!--&lt;br /&gt;        &amp;lt;Manager className="org.apache.catalina.ha.session.DeltaManager"&lt;br /&gt;                 expireSessionsOnShutdown="false"&lt;br /&gt;                 notifyListenersOnReplication="true"/&amp;gt;&lt;br /&gt;        --&amp;gt;&lt;br /&gt;        &amp;lt;Channel className="org.apache.catalina.tribes.group.GroupChannel"&amp;gt;&lt;br /&gt;          &amp;lt;Membership className="org.apache.catalina.tribes.membership.McastService"&lt;br /&gt;                      address="&lt;span class="Apple-style-span"  style="color:#FF0000;"&gt;&lt;b&gt;228.0.0.23&lt;/b&gt;&lt;/span&gt;"&lt;br /&gt;                      port="45564"&lt;br /&gt;                      frequency="500"&lt;br /&gt;                      dropTime="3000"/&amp;gt;&lt;br /&gt;          &amp;lt;Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"&lt;br /&gt;                    address="auto"&lt;br /&gt;                    port="5000"&lt;br /&gt;                    selectorTimeout="100"&lt;br /&gt;                    maxThreads="6"/&amp;gt;&lt;br /&gt;&lt;br /&gt;          &amp;lt;Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"&amp;gt;&lt;br /&gt;            &amp;lt;Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/&amp;gt;&lt;br /&gt;          &amp;lt;/Sender&amp;gt;&lt;br /&gt;          &amp;lt;Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/&amp;gt;&lt;br /&gt;          &amp;lt;Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/&amp;gt;&lt;br /&gt;          &amp;lt;Interceptor className="org.apache.catalina.tribes.group.interceptors.ThroughputInterceptor"/&amp;gt;&lt;br /&gt;        &amp;lt;/Channel&amp;gt;&lt;br /&gt;&lt;br /&gt;        &amp;lt;Valve className="org.apache.catalina.ha.tcp.ReplicationValve"&lt;br /&gt;               filter=".*\.gif;.*\.js;.*\.jpg;.*\.png;.*\.htm;.*\.html;.*\.css;.*\.txt;"/&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span"  style="color:#6633FF;"&gt;          &amp;lt;Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"&lt;br /&gt;                  tempDir="/tmp/war-temp/"&lt;br /&gt;                  deployDir="/tmp/war-deploy/"&lt;br /&gt;                  watchDir="/tmp/war-listen/"&lt;br /&gt;                  watchEnabled="false"/&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &amp;lt;ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/&amp;gt;&lt;br /&gt;      &amp;lt;/Cluster&amp;gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;/div&gt;&lt;div&gt;It's completely possible to get high-availibility load balancing and clustering working for Apache's HTTPd and Tomcat under Linux. The performance and fault-tolerant benefits are completely worth it. and thanks to a lot of work done by a lot of dedicated people, it's pretty easy to get set up and running. I send my thanks to every site I Googled figuring out how to get this working - there are too many to count. If you have questions or corrections, please post them here, and I'll do what I can to help figure them out.&lt;/div&gt;&lt;/div&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/15139516-5261478602597023849?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/5261478602597023849/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=5261478602597023849' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/5261478602597023849'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/5261478602597023849'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2009/06/apache-ssl-and-tomcat-clustering.html' title='Apache, SSL and Tomcat Clustering'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-3167671859965218787</id><published>2007-07-16T11:15:00.001-04:00</published><updated>2007-07-16T11:25:09.104-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>Unix File Permissions</title><content type='html'>A useful tidbit that I just discovered:  if you want to set unix file permissions for the group to match the owner it's as simple as:&lt;br /&gt;&lt;br /&gt;chmod g=u filename&lt;br /&gt;&lt;br /&gt;This just saved me boatloads of time.&lt;br /&gt;&lt;br /&gt;Discovered at: &lt;a href="http://groups.google.com/group/comp.unix.questions/browse_thread/thread/d5510a3c9c643d20/34e6488f108e39de%2334e6488f108e39de"&gt;comp.unix.questions &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;-Bill&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-3167671859965218787?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/3167671859965218787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=3167671859965218787' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/3167671859965218787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/3167671859965218787'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2007/07/unix-file-permissions.html' title='Unix File Permissions'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-6320217931732975340</id><published>2007-05-17T19:34:00.000-04:00</published><updated>2007-05-17T19:37:22.106-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='RailsConf2007'/><title type='text'>Harnessing Capistrano</title><content type='html'>Harnessing Capistrano - RailsConf2007 Tutorial&lt;br /&gt;Jamis Buck&lt;br /&gt;&lt;br /&gt;Slide aand demos available &lt;a href="http://presentations.jamisbuck.org/railsconf2007/"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Started out as a Deployment tool&lt;br /&gt;&lt;br /&gt;Can also use it for:&lt;br /&gt;+ monitoring tool across all servers (ps, df, uname, etc.)&lt;br /&gt;+ server maintenance (mounts, symlinks, ...)&lt;br /&gt;+ troubleshooting&lt;br /&gt;&lt;br /&gt;A basic config file and a demo.&lt;br /&gt;&lt;br /&gt;A config using a gateway and a demo.&lt;br /&gt;&lt;br /&gt;A cool description and demo of 'cap -e shell' which creates and caches a connection to each deployment server. Can be scoped by role or host.&lt;br /&gt;&lt;br /&gt;Cap 2.0 adds namespaces for tasks. Sort of a way of grouping like tasks. (eg. cap tail:showuploadlog groups the showuploadlog task into the tail namespace.&lt;br /&gt;&lt;br /&gt;Can do variables.&lt;br /&gt;&lt;br /&gt;And transactions, so you can make sure tasks complete on all servers or it rolls them all back - no inbetween. No good way to recover if a rollback fails.&lt;br /&gt;&lt;br /&gt;All sort of options for including other cap files. This was always optional in Cap 1.x, but there is no default config in Cap2.0.&lt;br /&gt;&lt;br /&gt;You can also script-check dependencies (cap deploy:check). That looks damn useful.&lt;br /&gt;&lt;br /&gt;scrpit/spin tell cap how to start your application Many times i's script/process/spawner&lt;br /&gt;&lt;br /&gt;cap deploy:cold &lt;- first time deploy&lt;br /&gt;&lt;br /&gt;cap deploy:rollback&lt;br /&gt;cap deploy:migrations&lt;br /&gt;cap deploy:web:disable&lt;br /&gt;cap deploy:web:enable&lt;br /&gt;cap deploy:cleanup &lt;- clean up all but the last 5 releases&lt;br /&gt;                     &lt;br /&gt;In addion to standard a version control-based checkout, you can do other types: export, copy, remote cache, or you can roll your own.  (Set by :deploy_via option.)&lt;br /&gt;&lt;br /&gt;There are all sorts of nifty options to get Cap to work with all sorts of version control systems.&lt;br /&gt;&lt;br /&gt;Lots of helpers: run, sudo, stream, connect, find_task, etc.&lt;br /&gt;&lt;br /&gt;In the ways of advanced usage, there are:&lt;br /&gt; + before_ and after_ events (before_deploy: run_tests)&lt;br /&gt; + custom callbacks: (email notifications, etc.) with complex rules&lt;br /&gt; + staging environments: you can script deploys to behave differently based on target&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-6320217931732975340?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/6320217931732975340/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=6320217931732975340' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/6320217931732975340'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/6320217931732975340'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2007/05/harnessing-capistrano.html' title='Harnessing Capistrano'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-1864382604326496304</id><published>2007-05-17T17:51:00.000-04:00</published><updated>2007-05-17T18:02:04.162-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='RailsConf2007'/><title type='text'>JRuby on Rails Tutorial</title><content type='html'>JRuby on Rails - RailsConf 2007 Tutorial&lt;br /&gt;&lt;br /&gt;Advantages:&lt;br /&gt;Take advantages of existing Java libraries&lt;br /&gt;Run on Java infrastructure&lt;br /&gt;Supports Java's multibyte/unicode strings.&lt;br /&gt;Support Ruby's thread API, one native thread = one system thread, good multicore use.&lt;br /&gt;True concurrent threading.&lt;br /&gt;&lt;br /&gt;Great progress in the past year. From barely running rails 1.1 last year to running all of rails (except some lowlow demand dark corners) now, with good performance on everything but RDoc.&lt;br /&gt;&lt;br /&gt;Most Gems just work. Anything pure Ruby (or with a JRuby port) runs.&lt;br /&gt;Webrick works.&lt;br /&gt;JRuby port of Mongrel works.&lt;br /&gt;&lt;br /&gt;Differences:&lt;br /&gt;1-Database support&lt;br /&gt;Pure Ruby drivers work - mySQL&lt;br /&gt;All the JDBC drivers you'd want to use work. (Yay!) (Some need custom coding to support migrations.&lt;br /&gt;JDNI for connection pooling (More yay!)&lt;br /&gt;&lt;br /&gt;2-No Native Extensions&lt;br /&gt;Unless there's a port.&lt;br /&gt;Mongrel - done&lt;br /&gt;Hpricot - done&lt;br /&gt;Database support - some done, some in progress&lt;br /&gt;RMagick - in progress&lt;br /&gt;&lt;br /&gt;3-Command-line Performance&lt;br /&gt;Very good (possibly faster) once you're running, but typical java-slow startup performance.&lt;br /&gt;&lt;br /&gt;Deployment&lt;br /&gt;1. Mongrel works well. No process forking, process management&lt;br /&gt;But why? Use Java App Servers via GoldSpike (or rails-integration) plugin.&lt;br /&gt;2. Build WAR files for Rails apps.&lt;br /&gt;One plugin, pure Ruby, out comes a deployable WAR file.&lt;br /&gt;3. Glassfish server gem. (Sort of a "Pack" in the box implementation.)&lt;br /&gt;Not yet. But soon.&lt;br /&gt;&lt;br /&gt;Migrating existing Rails apps to JRuby/Rails&lt;br /&gt;Be aware of the currently unsupported features.&lt;br /&gt;1. Database support&lt;br /&gt; - MySQL is great&lt;br /&gt; - Derby &amp; HSQL work well. Small embedable DB.&lt;br /&gt; - Postgres - Few out of 1000+ tests.&lt;br /&gt; - Oracle - starting to get attention&lt;br /&gt; - MS SQL Server &amp; DB - Need help, haven't really been worked on much.&lt;br /&gt; Migrations mostly work well, tricky on some DBs that don't have all features.&lt;br /&gt; Fixtures work well, parts of rails test. Issues generally YAML rather than DB issues.&lt;br /&gt;2. Native Extentions&lt;br /&gt; Option 1 - Use something else, aka don't do it.&lt;br /&gt; Option 2 - Use an equivalent Java library. (binds you to jruby)&lt;br /&gt; Option 3 - Port the library yourself.&lt;br /&gt; Option 4 - Port by wrapping a java library.&lt;br /&gt;3. Deployment Options&lt;br /&gt; - Mongrel: works, but not the most efficient&lt;br /&gt; - Existing WebApp Server: good concurrency, clustering, resource pooling&lt;br /&gt; - Grizzly/GlassFish v3 option: lightweight, gem mongrel-like install&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-1864382604326496304?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/1864382604326496304/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=1864382604326496304' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/1864382604326496304'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/1864382604326496304'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2007/05/jruby-on-rails-tutorial.html' title='JRuby on Rails Tutorial'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-1269530346012100374</id><published>2007-01-22T22:24:00.000-05:00</published><updated>2007-01-22T22:39:08.015-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='drop-down menus'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby on rails'/><title type='text'>Cascading Drop-downs in Rails</title><content type='html'>We were looking for a solid example of cascading dynamic drop-down select lists to use in our rails application, and found the web sorely lacking in solid examples. We found a very good start at &lt;a href="http://www.railsweenie.com/forums/2/topics/767"&gt;http://www.railsweenie.com/forums/2/topics/767&lt;/a&gt; but it wasn't complete enough, or didn't work entirely. So my very good buddy and co-worker Sheri and I figured this out, and got it working for our app, and wanted to document it here in the hopes that it helps someone else.  Here's the nutshell version:&lt;br /&gt;&lt;br /&gt;It's probably &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-corrected" id="SPELLING_ERROR_0"&gt;already&lt;/span&gt; there, but make sure this line is in your &lt;span style="font-weight: bold;"&gt;standard_layout.&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;rhtml&lt;/span&gt;&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&lt;%= javascript_include_tag :defaults %&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Add this function def to &lt;span style="font-weight: bold;"&gt;application_helper.&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;rb&lt;/span&gt;&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt; &lt;span style="font-family: courier new;"&gt;def update_select_box( target_&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;dom&lt;/span&gt;_id, collection, options={} )&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;    # Set the default options&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;    options[:text] ||= 'name'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;    options[:value] ||= 'id'&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;    options[:include_blank] ||= true&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;    options[:clear] ||= []&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;    &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;pre&lt;/span&gt; = options[:include_blank] ? [['','']] : []&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;    out = "update_select_options( $('" &lt;&lt; onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;dom&lt;/span&gt;_id.to_s &lt;&lt; "'),"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;    out &lt;&lt; "#{(&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;pre&lt;/span&gt; + collection.collect{ |c| [c.send(options[:text]), c.send(options[:value])]}).to_json}" &lt;&lt; ","&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;    out &lt;&lt; "#{options[:clear].to_&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_7"&gt;json&lt;/span&gt;} )"&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This calls update_select_options which needs to go into &lt;span style="font-weight: bold;"&gt;application.&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_8"&gt;js&lt;/span&gt;&lt;/span&gt;:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;function update_select_options( target, opts_array, clear_select_list ) {&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;    if( $(target).type.match("select" ) ){ // Confirm the target is a select box&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;        // Remove existing options from the target and the clear_select_list&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;        clear_select_list[clear_select_list.length] = target // Include the target in the clear list&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;        for( k=0;k &lt;&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;            obj = $(clear_select_list[k]);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;            if( obj.type.match("select") ){&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;                &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_9"&gt;len&lt;/span&gt; = obj.childNodes.length;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;                for( var i=0;i &lt;&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;            }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;        }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;        // Populate the new options&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;        for(i=0;i &lt;&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;            o = document.&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_10"&gt;createElement&lt;/span&gt;( "option" );&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;            o.&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_11"&gt;appendChild&lt;/span&gt;( document.&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_12"&gt;createTextNode&lt;/span&gt;( opts_array[i][0] ) );&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;            o.&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_13"&gt;setAttribute&lt;/span&gt;( "value", opts_array[i][1] );&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;            obj.&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_14"&gt;appendChild&lt;/span&gt;(o);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;        }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;    }&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Add something like this to the &lt;span style="font-weight: bold;"&gt;form.&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_15"&gt;rhtml&lt;/span&gt;&lt;/span&gt; (changing the name of the observable field as appropriate):&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&lt;%= observe_field 'item[facility_id]',  :frequency =&gt; 0.5, &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;         :update =&gt; 'location_id', :url =&gt; &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;         { :controller =&gt; 'item', :action=&gt; 'refreshLocation' }, &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;         :with =&gt; "'facility_id=' + escape(value)" %&lt;/span&gt;&gt;&lt;br /&gt;&lt;br /&gt;Add something like this to the controller:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;    def &lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_16"&gt;refreshLocation&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;      @facilities = Facility.find(:all)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;      @facility = Facility.find(&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_17"&gt;params&lt;/span&gt;[:facility_id])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;      @locations = Location.find_all_by_facility_id(&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_18"&gt;params&lt;/span&gt;[:facility_id])&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;      render :update do |page| &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;        page &lt;&lt; text =""&gt; :description} )&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;      end&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;    end&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This tidbit in the &lt;span style="font-weight: bold;"&gt;form.&lt;span onclick="BLOG_clickHandler(this)" class="blsp-spelling-error" id="SPELLING_ERROR_19"&gt;rhtml&lt;/span&gt;&lt;/span&gt; is the ultimate target of all this work (this is the drop down we want to refresh)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&lt;tr&gt;    &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;   &lt;td align="right"&gt;&lt;br /&gt;      &lt;label for="item_location_id"&gt;Location:&lt;/label&gt;&lt;br /&gt;   &lt;/td&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;   &lt;td&gt;&lt;br /&gt;      &lt;%= select_tag "item[location_id]", options_from_collection_for_select(@locations,:id,:description) %&gt;&lt;br /&gt;   &lt;/td&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;&lt;/tr&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt; &lt;br /&gt;&lt;span style="font-family: times new roman;"&gt;If I missed any code attributions from the various sources we pieced this together from, I'm sorry. Write me and I'll make good and give attribution where appropriate.&lt;br /&gt;&lt;br /&gt;If you have any questions about this, post 'em and I'll take my best shot at answering.&lt;br /&gt;&lt;br /&gt;And finally, thanks Sheri! Couldn't have done it without you!&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-1269530346012100374?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/1269530346012100374/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=1269530346012100374' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/1269530346012100374'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/1269530346012100374'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2007/01/cascading-drop-downs-in-rails.html' title='Cascading Drop-downs in Rails'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-116580583804524046</id><published>2006-12-10T21:36:00.000-05:00</published><updated>2006-12-10T21:57:22.446-05:00</updated><title type='text'>Rails Projects Abound</title><content type='html'>Okay, abound is a little strong. But we've got one new one and one to-finish &lt;a href="http://www.rubyonrails.com/"&gt;Ruby on Rails&lt;/a&gt; project on our plates at work. The new project is a quickie tracking app for all inventory items containing human or animal tissue that get used in the operating rooms. Apparently, it's a &lt;a href="http://www.jointcommission.org/"&gt;JCAHO&lt;/a&gt; requirement that, as of January 1, 2007, all hospitals have to track all items that contain any amount of tissue - we've always had to track tissue-based implants: heart valves, bone chips, etc. The idea that was being floated was for a MS Access app, but I've grown way too tired of supporting those, plus with Rails we can now code a web app as fast as we can do a desktop Access app, all things being equal.&lt;br /&gt;&lt;br /&gt;Politically speaking, it'll be very cool for us to pull this off in a two-week sprint, and as simple and straight-forward as it is, I'm thinking we'll be done much sooner, even with teaching &lt;a href="http://www.ruby-lang.org/"&gt;Ruby&lt;/a&gt; and &lt;a href="http://www.rubyonrails.com/"&gt;Rails&lt;/a&gt; to a couple of long-term Smalltalk/Java/Struts programmers. Technically speaking, the more Rails code we can get in production, the happier I'll be.&lt;br /&gt;&lt;br /&gt;The other Rails app on our near horizon is to spend some time finishing Tart, the request management app that I've been learning and twiddling with for over a year now. It's an sort of an issue tracker that incorporates multiple approvals (&lt;a href="http://en.wikipedia.org/wiki/Institutional_Review_Board"&gt;IRB&lt;/a&gt; and departmental), and is tuned to our request process. It'll be good to get it launched and out there in people's hands.&lt;br /&gt;&lt;br /&gt;Oh, the update I promised you in the last post? She interviewed very well, blew the socks off everyone she met, and she started on our team last week.&lt;br /&gt;&lt;br /&gt;-Bill&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Tags: &lt;a href="http://technorati.com/tag/rubyonrails" rel="tag"&gt;RubyOnRails&lt;/a&gt;, &lt;a href="http://technorati.com/tag/rails" rel="tag"&gt;Rails&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-116580583804524046?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/116580583804524046/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=116580583804524046' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/116580583804524046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/116580583804524046'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2006/12/rails-projects-abound.html' title='Rails Projects Abound'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-116180704237126285</id><published>2006-10-25T16:04:00.000-04:00</published><updated>2006-10-25T16:10:42.373-04:00</updated><title type='text'>Falling in love all over again</title><content type='html'>You know, when you're hiring, and the &lt;span style="font-weight: bold;"&gt;perfect&lt;/span&gt; resume comes across your desk, you know it? Well, it happened today. Literally the perfect candidate fell into our laps. I talked to her, and she sounds like &lt;span style="font-weight: bold;"&gt;exactly&lt;/span&gt; (really, &lt;span style="font-style: italic;"&gt;exactly&lt;/span&gt;.) what I'm looking for. She's got struts experience. She's got hibernate experience. She's got a serious value for test-driven development. Hell, she's got values as a programmer! She likes, understands, and wants to work in an Agile shop. From talking to her she's got a pretty laid-back personality. I think I'm in love.&lt;br /&gt;&lt;br /&gt;Her interview is set up for Friday, and I'm really curious to see how she's going to click with the team.&lt;br /&gt;&lt;br /&gt;Well, world.... I'll keep you posted on how it goes.&lt;br /&gt;&lt;br /&gt;-Bill&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-116180704237126285?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/116180704237126285/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=116180704237126285' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/116180704237126285'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/116180704237126285'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2006/10/falling-in-love-all-over-again.html' title='Falling in love all over again'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-115737308497735375</id><published>2006-09-04T07:54:00.000-04:00</published><updated>2006-09-04T08:32:56.586-04:00</updated><title type='text'>Team Building as Magic</title><content type='html'>I've recently been tasked with staffing our group back up to it's fullest potential. Recent comings and goings have left us with 2 staffed and 4 open positions, not including mine. As I've been wading through resumes and talking with candidates something struck me: building a team from near-scratch like this has a great resemblance to the strategic card games &lt;a href="http://www.magicthegathering.com/"&gt;Magic&lt;/a&gt; and &lt;a href="http://pokemon-tcg.com/"&gt;Pokemon&lt;/a&gt;.  To start a game in one of these, you sort through your collection of cards (offensive, defensive, special use, environmental, etc.) and build a game deck of 15 or so cards that you play that game with. Part of the gameplay is luck, but far more of it is strategy, hinging on how you pick what cards go in your deck and how you play those cards as the game progresses. &lt;br /&gt;&lt;br /&gt;Building a team for success at work is no different. I have dozens of likely candidates as potential choices, each with their technical and personal strengths and weaknesses. The team that I put together will determine our success over the coming months and years. In addition to core technical competencies, I'm putting emphasis on professional developer talents such as testing and agile team experiences, and I'm banking that that emphasis will serve us well. Other traits I'm finding valuable are a passion for craft, meaning I want people to be passionate about doing things the right way (over just getting them done), and people skills over technical skills, meaning that I don't want an uber-guru who keeps everyone around themselves ticked-off or can't communicate. Ideally you want someone with all these skills and all the ideal traits in one package (or even a whole team of folks like that), but those folks as rare and hard to find as heros in Magic. I think a solid heterogeneous team with just the right blend of skills in a team that can work well together is far more realistic and achievable.&lt;br /&gt;&lt;br /&gt;I'm off to build my winning deck.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-115737308497735375?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/115737308497735375/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=115737308497735375' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115737308497735375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115737308497735375'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2006/09/team-building-as-magic.html' title='Team Building as Magic'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-115412329926416121</id><published>2006-07-28T17:31:00.000-04:00</published><updated>2006-07-29T01:20:52.930-04:00</updated><title type='text'>OSCON 06 Day 5 - Morning Sessions</title><content type='html'>&lt;B&gt;Session 1&lt;/B&gt;&lt;br /&gt;Open Source Performance Monitoring Tools, Tips and Tricks for Java&lt;br /&gt;Matt Secoske&lt;br /&gt;matt@secosoft.net&lt;br /&gt;&lt;br /&gt;Your project requires performance monitoring/planning/goals when your business requires it. You can do this through profiling, which is a focused look at system execution.  To plan for performance: 1) determine your goals, 2) create testing scenarios, 3) determine monitoring / profiling needs. 4) Integrate this into your development process, and finally 5) integrate it into your production environment.&lt;br /&gt;&lt;br /&gt;In planning, you need to know:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;expected total number of clients&lt;/li&gt;&lt;br /&gt;&lt;li&gt;expected peak total number of clients&lt;/li&gt;&lt;br /&gt;&lt;li&gt;most common tasks these clients will be doing&lt;/li&gt;&lt;br /&gt;&lt;li&gt;acceptable response time&lt;/li&gt;&lt;br /&gt;&lt;li&gt;how long will the data stay around&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;and what you want to monitor:&lt;br /&gt;hardware (web/app/db servers) cpu, memory, cache hit %, disk and network speed.&lt;br /&gt;java specific (gc, app metrics)&lt;br /&gt;&lt;br /&gt;JUnitPerf decorates JUnit test, great for benchmarking particular tests or test cases while refactoring, it's good at continuous performance testing, but not so good as a deployed monitoring solution. It's also available in JUnit 4.0 as an annotation.&lt;br /&gt;&lt;br /&gt;The Grinder is a clusterable performance tester. It can do stress, load, capacity and functional testing. It can proxy traffic for recording and playback later as part of tests.&lt;br /&gt;&lt;br /&gt;Apache JMeter does stress, load, capacity and functional testing. It's not clusterable and doesn't do proxy recording. It does have a plugin architecture for customization.&lt;br /&gt;&lt;br /&gt;Log file analysis is another way to monitor performance. Since you're writing to disk it can have a noticeable negative impact on the performance you're trying to monitor. It requires changes to your source code, and doesn't accurately reflect how expensive your operation was, only the time required to execute it.  If you're committed to logging, Aspects is a recommended way of doing it (AspectJ, AspectWerkz, Java Interactive Profiler, GlassBox Inspector).&lt;br /&gt;&lt;br /&gt;JFluid / NetBeans Profiler - part of Sun's new JVM profiling tool, and also part of the NetBeans Profiler Extension. It supports local and remote profiling, and provides limited JVM support (mainly 5.0 and up).&lt;br /&gt;&lt;br /&gt;On the other side, there's Eclipse TPTP. It does local and remote profiling, and requires a JVM agent for remote use.&lt;br /&gt;&lt;br /&gt;Matt then did a brief demo of TPT in Eclipse, and a deeper demo of the NetBeans profiler, exploring many of the in's and out's. &lt;br /&gt;&lt;br /&gt;He closed with a few tips and tricks:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Put in just enough metrics to get your performance measurements&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Performance Test != Production&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Real world data + real world usage patters + near-production environment = accurate benchmarks&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Keep a little monitoring in production.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Session 2&lt;/B&gt;&lt;br /&gt;Hacking Your Home Phone System (Year 2) - aka. Does the Phone Work Today?&lt;br /&gt;Brian Aker&lt;br /&gt;&lt;br /&gt;Terms:&lt;br /&gt;PBX - private branch exchange&lt;br /&gt;FXO - receives signal&lt;br /&gt;FXS - generates signal&lt;br /&gt;DMARK - demarcation point&lt;br /&gt;PSTN - public switched telephone network&lt;br /&gt;&lt;br /&gt;On wiring: put boxes in every wall, with 3/4 conduit (the largest you can fit&lt;br /&gt;in a wall). Metal boxes survive better and are easier to mount. Run&lt;br /&gt;electricity separately and cross at 90deg angles. More than 360deg worth of&lt;br /&gt;conduit turns makes it hard to run cable later. Finally, don't leave boxes&lt;br /&gt;empty for inspections - it worries inspectors.&lt;br /&gt;&lt;br /&gt;Asterisk is an open source project that creates all sorts of phone&lt;br /&gt;functionality in software (voicemail, conferencing, PBX, etc.) Digium makes&lt;br /&gt;the best hardware (and funds Asterisk development). Skip the cheaper cards, and&lt;br /&gt;get the TDM400P cards. Be careful of the FXO / FXS ports, as plugging the&lt;br /&gt;wrong one into the wrong place can blow up the card.&lt;br /&gt;&lt;br /&gt;Phone Instruments&lt;br /&gt;BudgetTone: $40, cheap, cheap sounding, voice CID&lt;br /&gt;Snom 190: $299, entirely scriptable via web services, can talk to multiple voip&lt;br /&gt;services, no buttons for lines&lt;br /&gt;Polycom SoundPoint: $200, good sound, buttons for lines, PoE, slow to boot&lt;br /&gt;&lt;br /&gt;Analog to Digital Devices&lt;br /&gt;Sipura 2000: 2port FXS analog adapter, $70&lt;br /&gt;Sipura 3000: FXS/FXO bi-directional adapters, $70&lt;br /&gt;&lt;br /&gt;The Computers: Any linux box will work.&lt;br /&gt;&lt;br /&gt;The Software: Asterisk is hard to setup. The extensions file is the key to it&lt;br /&gt;all. Plan on it taking a while.&lt;br /&gt;&lt;br /&gt;The next generation is Asterisk@Home. The CD boots, wipes the drives, and&lt;br /&gt;installs a working Asterisk based on Centos. It probably needs to be tweaked&lt;br /&gt;after it installs, but it works. Recently they changed the name of the product&lt;br /&gt;to Trixbox (which comes with SugarCRM built in).&lt;br /&gt;&lt;br /&gt;High-availability: MySQL Cluster - set up a second machine, cluster them. &lt;br /&gt;&lt;br /&gt;Mashups:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Front-door solenoid: unlock your doors via your phone&lt;/li&gt;&lt;br /&gt;&lt;li&gt;ipkall: free phone numbers!! (dial-a-song, a special number for certain&lt;br /&gt;callers)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;dial-a-monkey network&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Livejournal's mod_mp3: good for creative content, freaks out business people&lt;/li&gt;&lt;br /&gt;&lt;li&gt;AIM Bot: with the follow-to-phone feature, it sends IM messages when messages arrive (configurable for certain folks, if that suits you.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Hardware Vendors&lt;br /&gt;Digium - &lt;a href="http://www.digium.com/"&gt;http://www.digium.com/&lt;/a&gt;&lt;br /&gt;Polycom - &lt;a href="http://www.polycom.com/"&gt;http://www.polycom.com/&lt;/a&gt;&lt;br /&gt;Snom - &lt;a href="http://www.snom.com/"&gt;http://www.snom.com/&lt;/a&gt;&lt;br /&gt;Sipura - &lt;a href="http://www.sipura.com/"&gt;http://www.sipura.com/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Links&lt;br /&gt;&lt;a href="http://voip-info.org/"&gt;http://voip-info.org/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.asterisk.org/"&gt;http://www.asterisk.org/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.planetasterisk.org/"&gt;http://www.planetasterisk.org/&lt;/a&gt;&lt;br /&gt;FreeWorld Dialup - &lt;a href="http://www.freeworlddialup.com/"&gt;http://www.freeworlddialup.com/&lt;/a&gt;&lt;a href="http://krow.livejournal.com/"&gt;&lt;br /&gt;http://krow.livejournal.com/&lt;/a&gt; &lt;- Speaker's blog.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;-Bill&lt;br /&gt;&lt;br /&gt;Tags: &lt;a href="http://technorati.com/tag/OSCON06" rel="tag"&gt;OSCON06&lt;/a&gt;, &lt;a href="http://technorati.com/tag/asterisk" rel="tag"&gt;Asterisk&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-115412329926416121?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/115412329926416121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=115412329926416121' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115412329926416121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115412329926416121'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2006/07/oscon-06-day-5-morning-sessions.html' title='OSCON 06 Day 5 - Morning Sessions'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-115410407358020469</id><published>2006-07-28T12:20:00.000-04:00</published><updated>2006-07-28T12:37:00.093-04:00</updated><title type='text'>OSCON 06 Day 4 - Afternoon Sessions</title><content type='html'>&lt;B&gt;Session 3&lt;/B&gt;&lt;br /&gt;Outer Joins for Fun and Profit&lt;br /&gt;Bill Karwin&lt;br /&gt;&lt;br /&gt;I've never fully understood Outer Joins. Although I can use them, I'm far more comfortable falling back to the (+) from Oracle and += from MSSQL to get what I want. Bill started explaining outer joins with set theory and Venn diagrams, which open source databases support which kind of outer joins. Then Bill launched into several examples, each explaining concepts of increasing complexity. Beyond the obvious stuff, you can do things like mimic a NOT IN subquery on platforms (MySql4.0) that doesn't support subqueries. You can also do a 'Greatest row per group' subquery without having to use the max() function. This lets you return more rows than just the max'd one. In english, its "show the row for which no other row exists with a greater date for the same product".&lt;br /&gt;&lt;br /&gt;One  of the more interesting demo/solution bits he showed was sudoku solving with SQL (using outer joins, of course...).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Session 4&lt;/B&gt;&lt;br /&gt;Using the Google Web Toolkit&lt;br /&gt;Bruce Johnson and Bret Taylor&lt;br /&gt;&lt;br /&gt;Ajax, the same old arguments: nothing to download, every app is only a URL&lt;br /&gt;away, desktop-like functionality. In reality, it auto-reinstalls every&lt;br /&gt;full-page load, so it'd better be small. It's sorta secure; it's so dang hard&lt;br /&gt;to get it working at all that security is almost an afterthought. There's a&lt;br /&gt;plethora of technologies, on it's own platforms with it's own quirks - it's no&lt;br /&gt;wonder developers hate Ajax. &lt;br /&gt;&lt;br /&gt;Looking at Ajax from a Java viewpoint, Google set out to leverage their Java&lt;br /&gt;knowledge, and make Ajax front-ends that are still very webby. They came up&lt;br /&gt;with the idea of translating Java into Javascript. They actually pulled this&lt;br /&gt;off, and it really works. GWT can run in two mode: hosted mode (the whole app&lt;br /&gt;runs in the jvm as java - this gives you java-based debugging - which is&lt;br /&gt;beautiful and useful!) and native mode (that runs in a os-native web browser). &lt;br /&gt;&lt;br /&gt;GWT moves all the stateful session logic to the client. This enables stateless&lt;br /&gt;load balancing and (thus) server clustering. It also leaves as much UI-only&lt;br /&gt;stuff on the client, requiring no server round-trip. GWT provides leverage for&lt;br /&gt;the solutions - you can take any solution and wrap it in a class and have it&lt;br /&gt;to reuse later.&lt;br /&gt;&lt;br /&gt;GWT, as Java, brings static type checking (static types... lalalalala....) and&lt;br /&gt;all the benefits and drawbacks that typically come with it. There are code reuse advantages: create your ajax libraries as jars, reuse your code.&lt;br /&gt;&lt;br /&gt;They provide a fantastic palette of UI widgets that make cross-platform rich&lt;br /&gt;UI creation pretty simple, and one less thing to worry about.&lt;br /&gt;&lt;br /&gt;The Web Toolkit comes along with a RPC library as well. It's dead simple, and&lt;br /&gt;lets you create objects remotely, and serialize them back to the browser.&lt;br /&gt;&lt;br /&gt;History and linking are ususally a casualty of Ajax. AWT lets you (with a pretty simple set&lt;br /&gt;of calls) provide a full history support and linking. It really is dead&lt;br /&gt;simple!&lt;br /&gt;&lt;br /&gt;But wait! There's more!!  There's JUnit support for testing of both the Java&lt;br /&gt;and Javascript sides!&lt;br /&gt;&lt;br /&gt;Downloadable at &lt;a href="http://code.google.com/webtoolkit/"&gt;http://code.google.com/webtoolkit/&lt;/a&gt;&lt;br /&gt; and the GWT Widget Library at &lt;a href="http://gwt-widget.sourceforge.net/"&gt;http://gwt-widget.sourceforge.net/&lt;/a&gt; and more.&lt;br /&gt;&lt;br /&gt; Wow. GWT is cool. I can't wait to get it and play with it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Session 5&lt;/B&gt;&lt;br /&gt;Taming an Audience with Laser and Snake&lt;br /&gt;Robert Stephenson&lt;br /&gt;&lt;br /&gt;The basic idea is that you have a webcam, an iSight in Robert's case, and a&lt;br /&gt;laserpointer. You point at the screen with the laserpointer, and the iSight&lt;br /&gt;and your software work like a mouse to control the computer running the&lt;br /&gt;presentation. In Robert's case, he leveraged the AppKit (a python interface to&lt;br /&gt;Apple system calls) to call the CocoaSequenceGrabber to capture frames. Robert&lt;br /&gt;chose a set of RGB factors to pick an appropriate range of laser colors to&lt;br /&gt;have the app look for.&lt;br /&gt;&lt;br /&gt;                 -1.1r + 2.0g -1.1b &gt; 0.9&lt;br /&gt;&lt;br /&gt;But this didn't work so well. The problem is that that there's not enough&lt;br /&gt;resolution in the sRGB color space for the camera to be able to resolve the&lt;br /&gt;dot of the laser pointer. And/or there's not enough range in the sRGB gamma&lt;br /&gt;when ends up clipping the intensity of the laser dot. Owing to the way that&lt;br /&gt;the camera's automatic gain control tries to normalize the camera exposure,&lt;br /&gt;Robert tweaked the camera's sensitivity settings to compensate and was&lt;br /&gt;able to reliably track the laser about the screen. &lt;br /&gt;&lt;br /&gt;He then, with some ObjectiveC, C, Python and AppleScript, he was able to&lt;br /&gt;control PowerPoint (or was it Keynote). Robert also added capabilties to&lt;br /&gt;ajust keystoning and skewing of the image for the camera. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Session 7&lt;/B&gt;&lt;br /&gt;Web Heresies&lt;br /&gt;Avi Bryany&lt;br /&gt;&lt;br /&gt;Seaside is a web development framework written in Smalltalk. Chances are&lt;br /&gt;you're not going to go out and use Seaside. That's okay with Avi. He just&lt;br /&gt;wants to talk about the heresies of web development. Avi's first idea is that&lt;br /&gt;the HTML files belong to the developer, and that designers work with the CSS&lt;br /&gt;files. He did a brief apples to apples comparison of language semantics so we&lt;br /&gt;non Smalltalk people could read his examples. Naming things, while a good idea&lt;br /&gt;when absolutely necessary, should be avoided if it can. You can use the object&lt;br /&gt;that the field is rendering for to source it's own information. If you don't&lt;br /&gt;name your objects, they get numbers, and a behind-the-scenes hash maps the&lt;br /&gt;numbers back to their objects and their accessors for each numbered field.&lt;br /&gt;Kinda confusing, but I can see the reasons why this would be a good idea.&lt;br /&gt;&lt;br /&gt;An aside from Avi's talk:  Seaside is based on Squeak Smalltalk, which I've been meaning to check out.&lt;br /&gt;From my Smalltalk days, grumble11grumble years ago, I remember the Virtual&lt;br /&gt;machine being HUGE and somewhat slow, and hard to crack for outside use. I'm&lt;br /&gt;really curious to give Seaside a shot, even if just to see how it does what it&lt;br /&gt;does. I'm intreagued as heck about how a Smalltalk web app server would fit&lt;br /&gt;together and work.&lt;br /&gt;&lt;br /&gt;Avi went on to cover a few demos, showing source code, and running a few hello&lt;br /&gt;world-type things. A question that came up (as part of his talk) is how would&lt;br /&gt;you serialize a session? The short answer is that you don't. Just use session&lt;br /&gt;affinity instead. The cost of a server failing and taking  a session with it&lt;br /&gt;tends to be very low, so (essentially) screw it. Use sessions like crazy. Put&lt;br /&gt;anything in a session you want. Really. Anytime. (I'm guessing this is another&lt;br /&gt;herasy.) Try on the idea that you can also save the current execution point&lt;br /&gt;and put it in the session. This would mean that you can throw the current&lt;br /&gt;execution stack in the session (and in this case putting it in the URL) as the&lt;br /&gt;form is displayed. When the form gets posted, you pull the execution stack out&lt;br /&gt;of the session and continue execution from where it was, thus processing the&lt;br /&gt;form. Think about that one and let that one sink in. Does your brain hurt yet?&lt;br /&gt;It should. (This overhead isn't that much, but amounts to about 1M / current&lt;br /&gt;user.)&lt;br /&gt;&lt;br /&gt;Another Seaside goodie is that you can change your page load order by changing&lt;br /&gt;the method order in your Smalltalk code. Relying on Smalltalk gives you some&lt;br /&gt;really interesting powers that I've never seen in web applications before.&lt;br /&gt;Your server image is still a fairly full-featured image, including the ability&lt;br /&gt;to vnc into the sever image and interactively debug it. That's another "Think&lt;br /&gt;about it." moment. That's cool.  &lt;br /&gt;&lt;br /&gt;&lt;B&gt;Q:&lt;/B&gt; What's the lineage of Seaside?&lt;br /&gt;&lt;B&gt;A:&lt;/B&gt; WebObjects + Paul Graham = Seaside. It's also similar to Tapestry.&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Q:&lt;/B&gt; What have been the changes to Seaside over time?&lt;br /&gt;&lt;B&gt;A:&lt;/B&gt; Seaside used to have a templating system. That was ditched 3 years&lt;br /&gt;or so ago in favor of the programamtic html creation. There were also a few&lt;br /&gt;minor architectural changes over time as well. &lt;br /&gt;&lt;br /&gt;&lt;B&gt;Q:&lt;/B&gt; Ajax?&lt;br /&gt;&lt;B&gt;A:&lt;/B&gt; Yes! http://scriptaculous.seaside.st/&lt;br /&gt;&lt;br /&gt;-Bill&lt;br /&gt;&lt;br /&gt;Tags: &lt;a href="http://technorati.com/tag/OSCON06" rel="tag"&gt;OSCON06&lt;/a&gt;, &lt;a href="http://technorati.com/tag/SQL" rel="tag"&gt;SQL&lt;/a&gt;, &lt;a href="http://technorati.com/tag/outer%joins" rel="tag"&gt;Outer Joins&lt;/a&gt;, &lt;a href="http://technorati.com/tag/smalltalk" rel="tag"&gt;Smalltalk&lt;/a&gt;,&lt;a href="http://technorati.com/tag/seaside" rel="tag"&gt;seaside&lt;/a&gt;,&lt;a href="http://technorati.com/tag/gwt" rel="tag"&gt;GWT&lt;/a&gt;,&lt;a href="http://technorati.com/tag/Google Web Toolkit" rel="tag"&gt;Google Web Toolkit&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-115410407358020469?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/115410407358020469/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=115410407358020469' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115410407358020469'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115410407358020469'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2006/07/oscon-06-day-4-afternoon-sessions.html' title='OSCON 06 Day 4 - Afternoon Sessions'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-115402776767259233</id><published>2006-07-27T15:14:00.000-04:00</published><updated>2006-07-27T15:23:39.046-04:00</updated><title type='text'>OSCON 06 Day 4 - Morning Sessions</title><content type='html'>OSCON '06 - Thursday Morning Sessions&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Session 1&lt;/B&gt;&lt;br /&gt;Building Rails to Legacy Applications&lt;br /&gt;Robert Treat&lt;br /&gt;&lt;br /&gt;Of course greenfield applications are the easiest to use in Rails, but what about existing schema? How do you get rails to coexist and work successfully with Rails?&lt;br /&gt;&lt;br /&gt;One option is to use your database to help fix the problem.  Views can make legacy schema look far more rails-friendly. He demo'd that in PostgreSQL, you can create a rule that lets view write-backs work. Once you set these insert/update rules, you can treat your view as rails-friendly tables and thing should be far easier.&lt;br /&gt;&lt;br /&gt;Using the database requires less knowledge of Ruby and Rails, and it does make things look like more what you expect to see from book examples. It does have the major drawback of having to support two schema.&lt;br /&gt;&lt;br /&gt;The other option is to make rails smarter about how it  That is, you can use ruby code to manipulate Active Record to match our data model.  First off, edit your environment.rb to set &lt;pre&gt;pluralize_table_names = false&lt;/pre&gt;. Another adaptation to make is to tell your model to use a differently-named primary key. That way rails knows to look for foo_id rather than just id. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Slides at &lt;a href="http://www.brighterlamp.org/"&gt;http://www.brighterlamp.org/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Session 2&lt;/B&gt;&lt;br /&gt;I'm 200, You're 200&lt;br /&gt;David Sklar&lt;br /&gt;sklar@sklar.com&lt;br /&gt;&lt;br /&gt;In Web 2.0, we make lots of assumptions about other services being up, and who we can depend on and who we can't. So, what is your app dependent on? What contingencies do you have if it's not there.&lt;br /&gt;&lt;br /&gt;Dependency is needinging something that's not yours (eg. physical control, organizational control, intellectual control). This includes server dependencies (content, per-machine hardware and software, internal- and external-network calls, content created by others), code dependencies (who wrote it, how it works, who wrote the documentation, who knows where the documentation is wrong.), business dependencies (who supplies your feed, how many of your folks are in the National Guard, what are your copyright and patent risk, what's in your SLAs and are the penalties helpful?).&lt;br /&gt;&lt;br /&gt;You can mitigate real-time dependencies with planned modes of degradation based on data freshness, application features and read/write data. Avoid live web service calls when possible, instead making calls offline, which lets you sanity check and cache the results.  You can also create a local data store for when the remote service has blips. To degrade features gracefully segment your app into non-interdependent parts. Further, you can build code with the idea of having pluggable external dependencies - eg use map provider Y instead of G, or ad protocol G instead of X. APP, S3 and "ad html" are already starting to fill that need. Monitoring your dependencies to know when to switch needs to happen.&lt;br /&gt;&lt;br /&gt;In summary, examine your dependencies and your risk exposure, and plan for and mitigate those risks accordingly.&lt;br /&gt;&lt;br /&gt;Slides at &lt;a href="http://www.sklar.com/"&gt;http://www.sklar.com/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;-Bill&lt;br /&gt;&lt;br /&gt;Tags: &lt;a href="http://technorati.com/tag/OSCON06" rel="tag"&gt;OSCON06&lt;/a&gt;, &lt;a href="http://technorati.com/tag/java" rel="ruby%on%rails"&gt;Ruby on Rails&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-115402776767259233?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/115402776767259233/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=115402776767259233' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115402776767259233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115402776767259233'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2006/07/oscon-06-day-4-morning-sessions.html' title='OSCON 06 Day 4 - Morning Sessions'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-115395921760319102</id><published>2006-07-26T20:11:00.000-04:00</published><updated>2006-07-26T20:13:37.616-04:00</updated><title type='text'>OSCON 06 Day 3 - Afternoon Sessions</title><content type='html'>&lt;B&gt;Session 1&lt;/B&gt;&lt;br /&gt;Driving Rails Deep into the Back Office&lt;br /&gt;Fernandez&lt;br /&gt;&lt;br /&gt;Financial presures to reduce costs are big.&lt;br /&gt;Be ready for the build vs. buy decision because it will come up.&lt;br /&gt;Obstacles: "good enough" legacy systems, BI tools, reporting packages/&lt;br /&gt;Better and faster in rails? Yes!&lt;br /&gt;&lt;br /&gt;One idea: the trojan application.&lt;br /&gt;&lt;br /&gt;The Race approach: parallel or same teams do the same project in rails and java/.net/whatever.&lt;br /&gt;&lt;br /&gt;The Pilot: do a small bit of an app as a proof of concept.&lt;br /&gt;&lt;br /&gt;The Rescue: catch a failing project and redo it in rails&lt;br /&gt;&lt;br /&gt;The Undercut: (risky) Come it, and because you're so productive you can do it in far less time than other solutions.&lt;br /&gt;&lt;br /&gt;Case Study:&lt;br /&gt;the "PCS" Project&lt;br /&gt;-Small team&lt;br /&gt;-Three months&lt;br /&gt;-Replaced homegrown PL/SQL solution&lt;br /&gt;-DSL-centric solution&lt;br /&gt;&lt;br /&gt;Lesson 1: optimize and raise your levels of abstraction&lt;br /&gt;How? Know what each piece of the stack does and doing a custom DSL.&lt;br /&gt;&lt;br /&gt;Lesson 2: Rails really breathes life into XP&lt;br /&gt;All the benefits of XP really click with Rails.&lt;br /&gt;&lt;br /&gt;Lesson 3: Don't sweat performance and scaling. Most back-office sysmts have relatively few users.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Session 2&lt;/B&gt;&lt;br /&gt;Streamlined&lt;br /&gt;Stuart Halloway&lt;br /&gt;Relevance LLC&lt;br /&gt;&lt;br /&gt;In the beginning, there was ruby and rails...&lt;br /&gt;Along came Streamlined, which replaces the basic scaffolding.&lt;br /&gt;&lt;br /&gt;Streamlined is generates pages with a basic stylesheet, ajaxified creation/edit/update/delete, on the fly filtering of all fields, sort by clicking column headers, export to csv/xml, &lt;br /&gt;&lt;br /&gt;New 'app/streamlied' directory that contains view code (that looks amazingly like rails models). It picks up object relationships from the rails models, and reflects them in the ajaxy goodness.  But the new streamlined directory contains all the view-related stuff and putting it in one place.  All the views (.rhtmls) are generated and fully open to being customized. If there's no customization, it &lt;br /&gt;&lt;br /&gt;All the CRUD stuff gets refactored from each and every controller up to the streamlined_controller.  All are overridable if need be. Or, if deleted entirely, there's an automatic fallback to a functional generic version.&lt;br /&gt;&lt;br /&gt;Streamlined is Alpha&lt;br /&gt;0.02 is out now&lt;br /&gt;0.03 is out next Monday&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Streamlined produces:&lt;br /&gt;-Production-ready Enterprise scaffolding.&lt;br /&gt;-Generic enterprise CRUD&lt;br /&gt;-Simplicity of ActiveRecord for views and controllers&lt;br /&gt;&lt;br /&gt;Generator options fall into 3 categories: &lt;br /&gt;-semantic (--no-relationships, --no-views)&lt;br /&gt;-look (--no-header, --no-about)&lt;br /&gt;-theme (css=vendor.css or whatever)&lt;br /&gt;&lt;br /&gt;UI Options&lt;br /&gt;-relationship - choose view and summary&lt;br /&gt;-user_columns - which columns to display&lt;br /&gt;&lt;br /&gt;Licensed under MIT license.&lt;br /&gt;&lt;br /&gt;Download http://www.streamlinedframework.org/&lt;br /&gt;Documentation: same place&lt;br /&gt;Submit patches: http://collaboa.streamlinedframework.org/&lt;br /&gt;&lt;br /&gt;Things to look at:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://jmatter.org/"&gt;jmatter&lt;/a&gt; - naked objects project&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Session 3&lt;/B&gt;&lt;br /&gt;Ruby for Java Programmers&lt;br /&gt;Ugo Cei&lt;br /&gt;&lt;br /&gt;Why?&lt;br /&gt;-Leveraging exsiting Java libraries, source and infrastuctre&lt;br /&gt;&lt;br /&gt;How?&lt;br /&gt;-RubyJavaBridge &lt;a href="http://arton.no-ip.info/collabo/backyard/?RubyJavaBridge"&gt;http://arton.no-ip.info/collabo/backyard/?RubyJavaBridge&lt;/a&gt;&lt;br /&gt; Works best if you wrap Java methods in simpler wrappers.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;No mappomg for ruby iterators on java collections.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;No date convertors&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Weak ejb getter/setter property converters.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;-SWIG - &lt;a href="http://www.swig.org/"&gt;http://www.swig.org/&lt;/a&gt;&lt;br /&gt; Jakarta POI, nifty library for manipulating Microsoft OLE2 Office fiiles uses SWIG to provide Ruby bindings. (http://jakarta.apache.org/poi/poi-ruby.html)&lt;br /&gt;&lt;br /&gt;-JRuby - &lt;a href="http://jruby/codehaus.org"&gt;http://jruby/codehaus.org&lt;/a&gt;&lt;br /&gt; Not a bridge, but a 100% Java interpreter written in Java&lt;br /&gt; Gives ruby access to all Java libraries.&lt;br /&gt; No access to ruby extensions in C&lt;br /&gt; "Almost" able to run RubyGems and Rails&lt;br /&gt; Quick Development pace.&lt;br /&gt; Still slow compared to C Ruby, but quoting Charles O. Nutter &lt;i&gt;"I think it's now very reasonable to say we should beat C Ruby performance by the end of the year."&lt;/I&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Can use ruby "each" on Java collections&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Data type conversions&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Full support for JavaBean properties&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Using the IRuby interpreter, you can call Ruby methods from Java.&lt;br /&gt;&lt;br /&gt;-XML-RPC - It's possible to run the java and ruby process independently and enable them to communicate.&lt;br /&gt;&lt;br /&gt;-SOAP - It's possible to run the java and ruby process independently and enable them to communicate.&lt;br /&gt;&lt;br /&gt;Slides at &lt;a href="http://www.sourcesense.com/transfer/ruby_for_java_programmers.pdf"&gt;http://www.sourcesense.com/transfer/ruby_for_java_programmers.pdf&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;-Bill&lt;br /&gt;&lt;br /&gt;Tags: &lt;a href="http://technorati.com/tag/OSCON06" rel="tag"&gt;OSCON06&lt;/a&gt;, &lt;a href="http://technorati.com/tag/java" rel="java"&gt;OSCON06&lt;/a&gt;, &lt;a href="http://technorati.com/tag/ruby" rel="tag"&gt;ruby&lt;/a&gt;, &lt;a href="http://technorati.com/tag/streamlined" rel="tag"&gt;streamlined&lt;/a&gt;, &lt;a href="http://technorati.com/tag/jruby" rel="tag"&gt;jruby&lt;/a&gt;, &lt;a href="http://technorati.com/tag/ruby%on%rails" rel="tag"&gt;Ruby on Rails&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-115395921760319102?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/115395921760319102/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=115395921760319102' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115395921760319102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115395921760319102'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2006/07/oscon-06-day-3-afternoon-sessions.html' title='OSCON 06 Day 3 - Afternoon Sessions'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-115394144417554492</id><published>2006-07-26T15:16:00.000-04:00</published><updated>2006-07-26T19:15:48.313-04:00</updated><title type='text'>OSCON 06 Day 3 - Morning Keynotes and Sessions</title><content type='html'>&lt;B&gt;Keynotes&lt;/B&gt;&lt;br /&gt;&lt;br /&gt;Of the morning keynote speakers, Tim O'Reilly had the most interesting and thought-provoking stuff to share. His talk keyed around 5 points that are he thinks are shaping the Open Source landscape today.&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Architectures of Participation (aka. Web2.0) &lt;/li&gt;&lt;br /&gt;&lt;cite&gt;&lt;br /&gt;"When the best leader leads, the people say 'We did it ourselves!'"&lt;br /&gt;&lt;/cite&gt; - Lao Tzu&lt;br /&gt;This is about leveraging your community to make your product richer, like Amazon reviews, or Craigslist content.&lt;br /&gt;&lt;li&gt;Open Source Licenses are Obsolete&lt;/li&gt;&lt;br /&gt;Maybe a little overstated in its statement, what Tim is saying is that Open Source licenses become irrelevant if the code is changed and run on web server somewhere, and it never redistributed. The community needs to create a similar definition for Open Services.&lt;br /&gt;&lt;li&gt;Asymetric Competition&lt;/li&gt;&lt;br /&gt;While big companies have lots of money to throw at problems, it's the small companies that create out-of-the-box solutions that blind-side the big guys. Craigslist, with 20 employees, is #8 the top 10 Web compnies in terms of hits, and is &lt; 1% the size of any of it's listmates.&lt;br /&gt;&lt;li&gt;Operations as Advantage&lt;/li&gt;&lt;br /&gt;&lt;cite&gt;&lt;br /&gt;"Being on someone's platform is becoming the same as being hosted on their infrastructure&lt;br /&gt;&lt;/cite&gt; - Missed attribution to a MS employee&lt;br /&gt;&lt;li&gt;Open Data&lt;/li&gt;&lt;br /&gt;This, I think is the biggest of them. On an obvious level, it's about mashups. If people can get to your data (via APIs) they can do amazing things that you never dreamed of. On another level, it's about your own data, and where it lives, and who owns it, and how easy it is to get out. If it's on your hard drive, it's yours. If it's on comany X's servers, who really owns it and what happens if comany X blows up, or gets bought, or whatever. What happens to your data then?&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Random links from Tim's talk&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.seaside.st/"&gt;Seaside&lt;/a&gt; - A web app framework in Squeak Smalltalk&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href"http://www.ning.com/"&gt;Ning&lt;/a&gt; - A build-your-own-webapp for the masses&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.openfount.com/"&gt;OpenFount&lt;/a&gt; Web2.0 app service that creates 2.0ishness via GWT and uses Amazon S3 for storage.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.stumbleupon.com/"&gt;StumbleUpon&lt;/a&gt; - A new way of discovering web sites.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Session 1&lt;/B&gt;&lt;br /&gt;Metaprogramming Java with HiveMind and Javassist&lt;br /&gt;Howard Lewis Ship&lt;br /&gt;hlship@gmail.com&lt;br /&gt;&lt;br /&gt;Metaprogramming - Writing programs to write programs. Traditionally at compiletime: lexx, yacc, XDoclet (javadoc-style comments that affect program build), AspectJ.&lt;br /&gt;&lt;br /&gt;Solution 1: Source Code Generation - Generate more source code at build time (XDoclet, ejbc) Awkward to write test to code that may not exists. Has a more complex build cycle.&lt;br /&gt;&lt;br /&gt;Solution 2: Aspect-Oriented Programming - Metaprogramming with AspectJ: weave your code in with ohter code in an "aspect". Cotrol how inital code and aspect code works together. It changes your classes at build-time by adding method calls interspersed with your code.&lt;br /&gt;&lt;br /&gt;Runtime Metaprogramming: Leave existing classes alone, and create new classes at runtime. Factories use configuraiton to create new classes and instantiate them. Done by Annotations.&lt;br /&gt;&lt;br /&gt;HiveMind: Inversion-of-control container, much like Spring. Provides lifecycle to services: injection of dependencies, notification of lifecycle events. Driven by XML configuration. AOL via interceptors wrap implementations.&lt;br /&gt;&lt;br /&gt;Javassist: APO library: Locat classes into memory as CtClass, modify them: add, remove, change, convert into Class objects. Don't have to learn bytecode ... Java-like syntax. (Part of JBoss)&lt;br /&gt;&lt;br /&gt;HiveMind wrappers: Doesn't change existing classes, lots of proxies. ClassFactory is a simplified API wrapper around ClassFactory. Create new ClassFab instance, and add constructior, interfaces, fields to that instance.&lt;br /&gt;&lt;br /&gt;Metaprogramming and Design Patterns - meta makes it easy to build code that implements useful design patterns easily (and more extisibly) on the fly.&lt;br /&gt;&lt;br /&gt;-Bill&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Session 2&lt;/B&gt;&lt;br /&gt;Embedding a Database in the Browser&lt;br /&gt;David Van Couvering&lt;br /&gt;Database Technology Group - Sun Microsystems&lt;br /&gt;david.vancouvering@sun.com&lt;br /&gt;&lt;br /&gt;A database? In a browser? Why?&lt;br /&gt;This is useful because....: the mobile user, keep personal data off the server, and it provides a fast local web cache.&lt;br /&gt;&lt;br /&gt;What do you need to make this work: Java, embedable, small-footprint, standards compliant, secure, and provide automatic crash recovery.&lt;br /&gt;&lt;br /&gt;Why a database? Standard, portable API (ODBC), ACID semantics, flexible data model, powerful query capability, and works with lots of tools, using existing skills.&lt;br /&gt;&lt;br /&gt;What is Apache Derby? 100% Java Open Source relational database, http://db.apache.org/derby. 2M jar, with options to get it down to 500k.&lt;br /&gt;&lt;br /&gt;Is this AJAX? Well, not really. All data is stored in the local derby implementation. Maybe LJAX?&lt;br /&gt;&lt;br /&gt;To actually use this, your code has to be cert signed (to get acces to the local filesystem for the local databae). Can self-sign or get a real cert from the usual places. Your code then needs to be wrapped in a PrivilegedAction block.&lt;br /&gt;&lt;br /&gt;Mapping Data to Fields: Java API with XML abstraction, call JDBC from Javascript, Java Persistance API, dojo storage abstraction, &lt;br /&gt;&lt;br /&gt;Essentially: SQL results -&gt; XML -&gt; js into DOM fields.&lt;br /&gt;&lt;br /&gt;You can even run this off of a USB stick. The derby format is portable and can be ready by any derby.jar. You can encrypt databases for security.&lt;br /&gt;&lt;br /&gt;Alternate solutions: mozStorage (browser specific, intended for internal Mozilla use), dojo.storage (with Flash)&lt;br /&gt;&lt;br /&gt;Future Directions: Web server in browser, Synchronization, Implement a dojo StorageProvider.&lt;br /&gt;&lt;br /&gt;-Bill&lt;br /&gt;&lt;br /&gt;Tags: &lt;a href="http://technorati.com/tag/OSCON06" rel="tag"&gt;OSCON06&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-115394144417554492?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/115394144417554492/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=115394144417554492' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115394144417554492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115394144417554492'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2006/07/oscon-06-day-3-morning-keynotes-and.html' title='OSCON 06 Day 3 - Morning Keynotes and Sessions'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-115389605935765223</id><published>2006-07-25T20:06:00.000-04:00</published><updated>2006-07-26T02:40:59.436-04:00</updated><title type='text'>OSCON 06 Day 2 - Afternoon Tutorials</title><content type='html'>John Paul Ashenfelter - Rock-solid Web Development: Testing Web Apps&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Take-home lesson #1 : Testing gives you confidence in your code and application.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Basic of Testing&lt;/b&gt;&lt;br /&gt;Hierarchy of testing:&lt;br /&gt;1. None.&lt;br /&gt;2. Ad hoc testing - depends on people, not reproducible.&lt;br /&gt;3. Unit testing&lt;br /&gt;4. Bodies - help desk, users, managers.. have people beat on it.&lt;br /&gt;5. Bodies + test plan - directs people on what to test.&lt;br /&gt;6. Automated test plans.- gives the computer the boring, tedious parts.&lt;br /&gt;&lt;br /&gt;Types of Testing: Low Level Code&lt;br /&gt;Low-level testing done by developers to make sure an object behaves the way the spec says it should.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Done by programmers&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Unit test are best examples&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Specific functionality testing in isolation.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Types of Testing: Application Level Testing (also Functional or Integrated Testing)&lt;br /&gt;Done by non-dev people... QA and UA types.&lt;br /&gt;Also may involve automated testing.&lt;br /&gt;Examples are browser interactions.&lt;br /&gt;&lt;br /&gt;Types of Testing: System Level&lt;br /&gt;Includes Load, Performance and Stress tests.&lt;br /&gt;&lt;br /&gt;Types of Testing: User Level&lt;br /&gt;Testing the stories from the cards - usability and acceptance testing.&lt;br /&gt;&lt;br /&gt;See also: conformance, security, and failover testing.&lt;br /&gt;&lt;br /&gt;Who does testing: You. Dev team, QA team, Help Desk. Not users / customers.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Take-home lesson #2: Do not let your users do your testing.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Getting started? If you're starting from ground zero (lotsa code, no tests) you can add tests as you write new code, add tests that demonstrate reported bugs, and add test instead of clicking through the app yet again.&lt;br /&gt;&lt;br /&gt;There's also (from http://use.perl.org/~amoore/journal/30215) &lt;br /&gt;1) boiling the frog - start slowly &lt;br /&gt;2) play ping-pong - write a test for some code that someone's else wrote&lt;br /&gt;3) maybe a rachet? - keep improving, and make sure you keep bettering your test standards.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Take-home lesson #3: Good programmer write tests.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Functional Testing with Selenium&lt;/b&gt;&lt;br /&gt;Easy to use, runs in many browsers, exposes browser specific issues, straightforward to automate.&lt;br /&gt;&lt;br /&gt;Speaking seleneese: you build tests in html as 3-column tables. The command language has Actions, Accessors, and Assertions. And there are locators and patterns.&lt;br /&gt;&lt;br /&gt;Actions: anything the user can do, there's a selenium action for.&lt;br /&gt;&lt;br /&gt;Accessors: examine the state of browser/application, usu store&lt;i&gt;Something&lt;/i&gt; each has several (often 6) related assertions.&lt;br /&gt;&lt;br /&gt;Assertions: &lt;br /&gt;assertSomething - aborts on a failure&lt;br /&gt;verifySomething logs failures and continues&lt;br /&gt;waitForSomething waits until a timeout or condition (ajax)&lt;br /&gt;There are also the inverse of all these.&lt;br /&gt;&lt;br /&gt;Seleneese locations: it can find things by id, name, identifier, link, dom, and xpath.&lt;br /&gt;&lt;br /&gt;Basic test structure is a html 3-column table: command | argument | argument, where the second argument is often blank. The first line is often a comment.&lt;br /&gt;&lt;br /&gt;There's also an IDE wich can record sessions show test, and allow editing of existing / recorded tests.&lt;br /&gt;&lt;br /&gt;Where this gets cool is that you can have something (Java, Rails, perl, etc.) generate the 3-column HTML that is your test. Doing this, you can build test that contain decisions, loops, and database referencing.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Take-home lesson #4: Selenium will save you time.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Continuous Integration and Automation&lt;/b&gt;&lt;br /&gt;Integration with Cruise Control and your testing is a good and worthwhile thing&lt;br /&gt;to do.&lt;br /&gt;&lt;br /&gt;Use dbUnit to set up your database before tests and reset it when you're&lt;br /&gt;finished. It's also useful for testing stored procedures and the like. You&lt;br /&gt;know, the smarts you put into db code.&lt;br /&gt;&lt;br /&gt;Take-home lesson #5: If you repeat it, automate it.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Load testing with Grinder&lt;/b&gt;&lt;br /&gt;Load tests - how does the app behave with more than one user using it.&lt;br /&gt;Performance tests - load it and measure performance metrics at typical usage&lt;br /&gt;levels.&lt;br /&gt;Stress tests - crank up the load until it breaks.&lt;br /&gt;&lt;br /&gt;The Grinder (Use V3 and not V2. The Beta label is bogus and is really only&lt;br /&gt;related to documentation):&lt;br /&gt;Agent: that run the tests&lt;br /&gt;Console: coordinate the tests in a central location&lt;br /&gt;TCPProxy: records the test using a browser.&lt;br /&gt;&lt;br /&gt;Personal note: Grinder does indeed rock - we've used it for some load-related&lt;br /&gt;troubleshooting. But I realize we could be using it for lots more.&lt;br /&gt;&lt;br /&gt;Talk slides are at http://www.ashenfelter.com/ or http://transitionpoint.com/&lt;br /&gt;&lt;br /&gt;Apologies for this one being so erratic and unedited. This is pretty raw dump of my notes from the session.&lt;br /&gt;&lt;br /&gt;-Bill&lt;br /&gt;&lt;br /&gt;Tags: &lt;a href="http://technorati.com/tag/OSCON06" rel="tag"&gt;OSCON06&lt;/a&gt;, &lt;a href="http://technorati.com/tag/testing" rel="tag"&gt;testing&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-115389605935765223?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/115389605935765223/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=115389605935765223' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115389605935765223'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115389605935765223'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2006/07/oscon-06-day-2-afternoon-tutorials.html' title='OSCON 06 Day 2 - Afternoon Tutorials'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-115385544526099600</id><published>2006-07-25T15:11:00.000-04:00</published><updated>2006-07-25T15:24:05.273-04:00</updated><title type='text'>OSCON 06 Day 2 - Morning Tutorials</title><content type='html'>Stuart Halloway - Ajax on Rails&lt;br /&gt;&lt;br /&gt;Stuart's smart and articulate, not to mention that he's a &lt;a href="http://relevancellc.com/main/about"&gt;hometown boy&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Many of the recent successful companies (google, yahoo...) didn't come from vendor-supplied solutions. They evolved on Open Source, by having a good idea, being early to the solution space, and pursuing it tenaciously. AJAX (and likely on Ruby on Rails) is a great enabler for &lt;br /&gt;&lt;br /&gt;97% of AJAX traffic on the web is html. But he lies. He pulls number out of his, erm.... thumb.&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Prototype, the Library&lt;/B&gt;&lt;br /&gt;It provides low level support for dynamic web apps, hiding browser oddities. It's used by Scriptaculous and Rico, and was driven and inspired by Ruby on Rails - the method names in it a RoRish already. It does xhr completely and provides some JS extensions. It does a bit of DOM and CSS/Behavior stuff, and in Stuart's model, it does 'view-centric' ajax (sending mostly html to the client).&lt;br /&gt;&lt;br /&gt;Next we demo'd a basic type-ahead search app (think GoogleSuggest). There is lots of space for compromise, from doing full ajax (lots of server interaction) to pulling all results to the browsers and filtering things entirely on the client in js; there's all sorts of space for compromises in between. Doing ajax is about taking granularity from one page at time to much smaller mini-requests. For this demo, we talked through the Rails call to do this, then looked at the js that was generated as a result of that call.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Random tool aside: The Green Checkmark is the &lt;A href="https://addons.mozilla.org/firefox/1843/"&gt;Firebug&lt;/A&gt; Firefox extension. Get it. Use it. It even has a Javascript debugger. Good mention too for the Web Developer toolbar.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;The server side of the demo is in Rails; it does the database-y bit that does the search, then returns a rendered partial to the page with the results.&lt;br /&gt;&lt;br /&gt;There are several useful XHR helper methods: link_to_remote, form_remote_tag, remote_form_for, observe_field, observe_form, submit_to_remote. These do a lot of common, useful, helperish stuff that you probably want to already be doing anyway.&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Degradable Ajax&lt;/B&gt;&lt;br /&gt;It's pretty straightforward to have an Ajax app behave like a conventional web1.0 app, albeit without the fancy typeheads and puffs and shrinks. If the ajax widgets call url foo, and the form submit url is foo, the action behind foo can be smart enough to know whether the request came from an ajax control, in which case it returns a partial, or if the form submits, it processes and returns the entire page.&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Scriptaculous&lt;/B&gt;&lt;br /&gt;&lt;a href="http://script.aculo.us/"&gt;Scriptaculous&lt;/a&gt; is a effects and widget library that builds on Prototype. It, too, is very Ruby- and Rails-ish in it's naming and parameters. The next demo and code review was a autocomplete field with Scriptaculous. The nextnext demo is a scriptaculous drag-and-drop demo. These libraries make it so darn easy those things that used to be such a pain.&lt;br /&gt;&lt;br /&gt;&lt;B&gt;RJS&lt;/B&gt;&lt;br /&gt;All the code so far (and both libraries) falls flat at changing more than one section of the page at once. RJS does this, and it does it by sending js back to the browser to be executed. To get started do &lt;b&gt;rake rail:update:javascripts&lt;/b&gt;. Add &lt;b&gt; javascript_include_tag :defaults&lt;/b&gt; in your template page to get all the right libraries in your page. When you call something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if @saved&lt;br /&gt;  page.visual_effect(:blind_up, 'model_form', :duration=&gt;0.5)&lt;br /&gt;  page.replace_html 'model_error', 'Saved!'&lt;br /&gt;else&lt;br /&gt;  page.replace_html 'model_error', error_messages_for('player')&lt;br /&gt;end&lt;br /&gt;page.delay(0.5) { page.redirect_to(:action=&gt;'list') }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;rather than the obvious-looking calls back to the client. It generate javascript and sends it back to the client to make these things happen.&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Streamlined&lt;/B&gt;&lt;br /&gt;Stuart, after doing DHH's 7 minute rails demo in 3 minutes, regenerated and threw the same screens up using Streamlined, his companies gee-whiz ajaxy rails generator. It's great. It's beautiful. It's wicked easy. And they're officially launching it here at OSCON (tomorrow, I think). What Stuart demo'd was just a little tiny taste of what it can do (as witnessed at RailsConf), and he'll going to floor everyone tomorrow at the conference session. &lt;br /&gt;&lt;br /&gt;&lt;i&gt;Random tool aside #2: &lt;a href="http://www.squarefree.com/shell/"&gt;JavasScript Shell&lt;/a&gt; another useful Firefox extension. It lets you run arbitrary js against your page on the fly.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;B&gt;More on Protype&lt;/B&gt;&lt;br /&gt;At a more raw javascript level, the prototype library helps make js behave very much like a real oo language, and helps smooth over the difference between different vendors browsers. Getting down to js at this level, while painful at time, is necessary to work around certain kinds of problems. Prototype's usefulness shouldn't be underestimated. It's not just for visual DOMish javascript, but it adds considerable Object (big O) support to javascript. &lt;br /&gt;&lt;br /&gt;Technologies from this talk to look more at: &lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://streamlined.relevancellc.com/"&gt;Streamlined&lt;/a&gt; - A fantastic ajax scaffold generator for rails.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://prototype-window.xilinus.com/"&gt;Prototype Window Class&lt;/a&gt; - a js library for doing windows.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;A href="http://glu.ttono.us/articles/2006/05/29"&gt;ARTS Testing Extension&lt;/a&gt; - good tool for testing rjs code&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Stuart's presentation slides and sample code is available at &lt;a href="http://www.codecite.com/"&gt;codecite.com&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;-Bill&lt;br /&gt;&lt;br /&gt;Tags: &lt;a href="http://technorati.com/tag/OSCON06" rel="tag"&gt;OSCON06&lt;/a&gt;, &lt;a href="http://technorati.com/tag/ajax" rel="tag"&gt;Ajax&lt;/a&gt;, &lt;a href="http://technorati.com/tag/rubyonrails" rel="tag"&gt;RubyOnRails&lt;/a&gt;, &lt;a href="http://technorati.com/tag/javascript" rel="tag"&gt;Javascript&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-115385544526099600?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/115385544526099600/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=115385544526099600' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115385544526099600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115385544526099600'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2006/07/oscon-06-day-2-morning-tutorials.html' title='OSCON 06 Day 2 - Morning Tutorials'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-115378652940913230</id><published>2006-07-24T20:12:00.000-04:00</published><updated>2006-07-24T20:15:29.420-04:00</updated><title type='text'>OSCON 06 Day 1 - Afternoon Tutorials</title><content type='html'>Mastering VIM - Damien Conway&lt;br /&gt;&lt;br /&gt;Alright, I've been in the tutorial session 5 minutes and I've given up on taking coherent notes. He's so smart, and so fast, and jumps around and covers material so quickly. I strongly prefer vi to emacs (I usually never install it). Over time, I've drifted away from vi, (I'm taking notes in and am just getting used to KDE's Kate) thinking, "Oh, it's too hard to worry with... Kate (or whatever) is so much easier". Damien has proven me wrong, and has shown me so many easy, powerful things to do with vim, that I'm excited and curious to give it a another go.&lt;br /&gt;&lt;br /&gt;My favorite bits were:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Using do-it-yourself marks and vim's native marks to make jumping and editing easier.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The idea of binding a key to the :nohilite to hide the search hilighting when you're done.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Actually *getting* cut(d)/copy(y)/paste(p) by the vi keys (I always wimp and drop back to mouse/xwin click-drag-middleclick to do it)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;]p to paste something at the current level of indentation&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Branched undo in vim7, producing the "Trousers of Time"&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Autocompletion!! (Well, I knew that it has always done it for filenames, but it can do it for vim commands and body text as well.) This gives us language-specific completions! &lt;/li&gt;&lt;br /&gt;&lt;li&gt;You can use vim as a file browser!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;set autowrite&lt;/b&gt; - always save before quit&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;:options|resize&lt;/b&gt; - browse and set all options, then you can :mkvimrec to make a .vimrc to save all your current settings.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;set shiftround&lt;/b&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Damien's Total Tabular Control function. Very nice.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Automatic, self-cleaning backup files.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Visual Block Mode - swoon.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;vipJ - Join all the lines in a paragraph together.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Abbreviations, tho they're not as good as insertion maps because they don't need the extra space after. This can be good and bad.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;:nmap &amp;lt;Space&amp;gt; &amp;lt;something useful&amp;gt;&lt;/b&gt; - Make normal-mode space do something useful.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;It was a great talk (a very difficult choice over the Asterix talk), and he's put together 50 tips - each a broad topic group of commands - in the handouts that are the real take-home goodies from this talk.&lt;br /&gt;&lt;br /&gt;And a quick word on Damien: he's a great presenter. I got hooked on him at last year's OSCON, where he redefined Perl to work in Latin, with Roman numerals for results.&lt;br /&gt;&lt;br /&gt;-Bill&lt;br /&gt;&lt;br /&gt;Tags: &lt;a href="http://technorati.com/tag/OSCON06" rel="tag"&gt;OSCON06&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-115378652940913230?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/115378652940913230/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=115378652940913230' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115378652940913230'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115378652940913230'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2006/07/oscon-06-day-1-afternoon-tutorials.html' title='OSCON 06 Day 1 - Afternoon Tutorials'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-115377992570346059</id><published>2006-07-24T18:18:00.000-04:00</published><updated>2006-07-24T18:25:43.916-04:00</updated><title type='text'>OSCON 06 Day 1 - Morning Tutorials</title><content type='html'>Scalable Internet Architectures - Theo Schlossnagle&lt;br /&gt;&lt;B&gt;Intro and Useful Points&lt;/B&gt; - (I'll backfill this intro as soon as I make it to my notes...)&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Practicals&lt;/B&gt; - The problem: Scalable static image serving - eg. an in-house Ikami. We looked at a vendor solution, +/- of it. Next we did a build-your-own, looking at each of the pieces, and scaling the solution bigger and smaller. We talked through configuring each of the pieces, including a cool look at how to decide which image server is closest to the user geographically. By using multiple DNS servers, one near each image serving cluster. Things should converge... but they don't. Routes change too quickly for this to work. This does work if you use DNS Shared IP (AnyCast). All 3 servers all claim to serve the same IP - you'll always get to the closest server, with no convergence time. You can use a similar technique (if you own enough of the right, big, colocated parts) to make your system DDOS-resistant.&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Logging&lt;/B&gt; - Again, we started with a hypothetical configuration and defined goals = multiple servers, real-time log analysis and reaction. Next he covered (dis)advantages of distributed logging, passive logging (sniffing), leading us to multicast logging. (JMS == perfect multicast logger at the app level) You can add multiple subscribers and loggers on the fly. This enables active and passive monitors, special purpose analyzers, and write-to-disk tasks.&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Caching Architectures&lt;/B&gt; - caching can benefit application performance (and perceived performance) greatly. Theo covered layered cache, integrated cache (in app), data cache (in data store), write-thru cache. As before, he created an example case with example system configuration and goals of the exercise. In our sample bloggy-newsy app, when an article request comes in, we check to see if that page exists on the server, and if not, we fetch it from the db and create the page locally, so it exists for all future requests - much like we already do at Duke with patient discharge dates.&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Tiered architectures&lt;/B&gt; - Theo started with a background on tiered design. Then he evolved into a worthy rant against traditional tiering. (Expensive people and $$-wise, hard to predict need and scale up/down.) A healthy replication system that allows any type of machine (web server) to answer any request. Scaling then just requires adding (or removing) like servers as needed. This sounds like the like of stuff that mongrel enables. Need more capability? Just stack it on like lego bricks.&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Database Replication&lt;/B&gt; (part of Tiering) - It's a hard thing to deal with and do well. This is not warm-(or cold-) failover - this is true db replication (clustering). Multimaster replication is a log way off. Master-slave stuff is ready to use now. He made a mention of multi-vendor database replication - using mysql to cache and crunch 100 distributed instances, all dumping their data to a single Oracle backend.&lt;br /&gt;&lt;br /&gt;&lt;B&gt;The Right Tool for the Job - aka. How to Do it Wrong&lt;/B&gt;  For our previous sample newsy-bloggy app, the customer wants a "which 30 users last loaded this page" and "which pages did a user load in the last 30 minutes". He talked through a mysql implementation of this, and how poorly it scales and how ludicrous the implementation would be. Next we created a custom app (a Skiplist structure in C) that hooks the logging stream (from above) and crunches it on the fly. &lt;br /&gt;&lt;br /&gt;&lt;B&gt;Q&amp;A Session&lt;/B&gt;&lt;br /&gt;&lt;B&gt;Q: &lt;/B&gt;&lt;I&gt;File systems?&lt;/I&gt;&lt;br /&gt;&lt;B&gt;A: &lt;/B&gt;There's GFS (at a cost)... Lustre is good. None are perfect, avoid them all if you can.&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Q: &lt;/B&gt;&lt;I&gt;Does spread have a performance cost?&lt;/I&gt;&lt;br /&gt;&lt;B&gt;A: &lt;/B&gt;No, negligible.&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Q: &lt;/B&gt;&lt;I&gt;I have huge sessions that resist distribution.&lt;/I&gt;&lt;br /&gt;&lt;B&gt;A: &lt;/B&gt;Go optimize them, make it smaller or compress it. Another option is to subdivide the session among different urls, and only get the big/expensive ones when you need them.&lt;br /&gt;&lt;br /&gt;&lt;B&gt;Q: &lt;/B&gt;&lt;I&gt;Massive databases, how do you scale them?&lt;/I&gt;&lt;br /&gt;&lt;B&gt;A: &lt;/B&gt;Most obvious way is to federate (subdivide by geography/age/etc) to different databases.&lt;br /&gt;&lt;br /&gt;Technologies from this talk to look more at: &lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.spread.org/"&gt;spread&lt;/a&gt; - a group message service&lt;/li&gt;&lt;br /&gt;&lt;li&gt;whackamole - provides load-balancing and failover&lt;/li&gt;&lt;br /&gt;&lt;li&gt;mod_log_spread - &lt;/li&gt;&lt;br /&gt;&lt;li&gt;spreadlogd&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Splash&lt;/li&gt; - distributed sessions for clusters of web servers&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Tags: &lt;a href="http://technorati.com/tag/OSCON06&lt;br /&gt;" rel="tag"&gt;OSCON06&lt;br /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-115377992570346059?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/115377992570346059/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=115377992570346059' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115377992570346059'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115377992570346059'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2006/07/oscon-06-day-1-morning-tutorials.html' title='OSCON 06 Day 1 - Morning Tutorials'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-15139516.post-115377266527432887</id><published>2006-07-22T16:07:00.000-04:00</published><updated>2006-07-24T16:24:25.290-04:00</updated><title type='text'>Welcome!</title><content type='html'>I've had this blog for a long while and have yet to post anything to it, intending for this to be my "professional" blog (counter to my thought-of-the-moment &lt;a href="http://schoolboyheart.blogspot.com/"&gt;personal blog&lt;/a&gt;), with all my geeky inspirational wisdom, and the right time never seems to come. Last month it was very close, with &lt;a href="http://railsconf.org/"&gt;RailsConf '06&lt;/a&gt; in Chicago, but the network support for the conference and the hotel were laughable, and my blogging urges were thwarted yet again.&lt;br /&gt;&lt;br /&gt;Now, I'm on my way to Portland, Oregon for &lt;a href="http://conferences.oreillynet.com/os2006/"&gt;OSCON '06&lt;/a&gt;.  I came &lt;a href="http://schoolboyheart.blogspot.com/2005/08/oscon-2005-day-1-rubyrails-tutorials.html"&gt;last year&lt;/a&gt; and had a blast. I got to see DHH do his Rails intro in person, I got to hear Why do a show, I got to ride a Segway, I got to meet a fair number of cool, geeky folks, and I got exposed to all sorts of wonderful, cool, nerdly ideas than I never would have if I hadn't come.&lt;br /&gt;&lt;br /&gt;This year, I'm going without my wife. While it was great exploring Portland and the surrounding environs with her, I think I missed out on some fun after-hours social events. This year (for better or worse....) I get to be as geeky as I want to be, 24x5. I'm really curious what kind of ideas I'm going to end up with by the end of this. &lt;br /&gt;&lt;br /&gt;So, welcome to my professional blog. I'm looking forward to sharing my experiences as a software development team lead, an explorer of new technologies, a Ruby on Rails enthusiast, and as a "veteran" software developer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/15139516-115377266527432887?l=bhansley.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bhansley.blogspot.com/feeds/115377266527432887/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=15139516&amp;postID=115377266527432887' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115377266527432887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/15139516/posts/default/115377266527432887'/><link rel='alternate' type='text/html' href='http://bhansley.blogspot.com/2006/07/welcome.html' title='Welcome!'/><author><name>Bill</name><uri>http://www.blogger.com/profile/00130436285949194021</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
