<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:blogChannel="http://backend.userland.com/blogChannelModule" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:pingback="http://madskills.com/public/xml/rss/module/pingback/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
  <channel>
    <title>LavaBlast Software Blog</title>
    <description>Help your franchise business get to the next level.</description>
    <link>http://blog.lavablast.com/</link>
    <docs>http://www.rssboard.org/rss-specification</docs>
    <generator>BlogEngine.NET 1.5.0.7</generator>
    <language>en-CA</language>
    <blogChannel:blogRoll>http://blog.lavablast.com/opml.axd</blogChannel:blogRoll>
    <blogChannel:blink>http://feeds.feedburner.com/LavablastSoftwareBlog</blogChannel:blink>
    <dc:creator>LavaBlast</dc:creator>
    <dc:title>LavaBlast Software Blog</dc:title>
    <geo:lat>0.000000</geo:lat>
    <geo:long>0.000000</geo:long>
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/LavablastSoftwareBlog" type="application/rss+xml" /><feedburner:emailServiceId>LavablastSoftwareBlog</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
      <title>Penniless Startup Founders</title>
      <description>&lt;p&gt;&lt;a href="http://www.flickr.com/photos/etremblay/3985619536/sizes/l/"&gt;&lt;img style="border-right-width: 0px; margin: 10px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Where will this path lead you?" border="0" alt="Where will this path lead you?" align="right" src="http://blog.lavablast.com/image.axd?picture=path.jpg" width="364" height="244" /&gt;&lt;/a&gt; This post is a follow up to one of our &lt;a href="http://blog.lavablast.com/post/2009/04/06/Software-Startup-Lessons-(Part-5)-ndash3b-Being-a-software-startup-in-a-recession.aspx"&gt;previous posts&lt;/a&gt; that discussed &lt;a href="http://blog.lavablast.com/post/2009/04/06/Software-Startup-Lessons-(Part-5)-ndash3b-Being-a-software-startup-in-a-recession.aspx"&gt;starting a software business during the recession&lt;/a&gt;. In this post, I want to focus on the cash flow aspects for very early stage software startups. A few years ago, we started the company with nothing in the bank and we've managed to not only survive but prosper regardless of today's tough economic conditions. &lt;strong&gt;It is possible to launch a software startup with no money: the tradeoff is time.&lt;/strong&gt; It will take longer to get out of the very early stages.&lt;/p&gt;  &lt;h1&gt;Context / Introduction&lt;/h1&gt;  &lt;p&gt;Before starting, I'd like to point out that the tips that follow are only valid in a particular context:&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Understand that these tips are for the very early stage&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Your first business goal is to get out of the very early stage as soon as possible. &lt;/li&gt;    &lt;li&gt;Lots of these tips concern petty little details. However, together these details matter when at the very early stage, when you're fighting for survival. &lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.flowventures.com/blog/index.php/2009/11/05/playing-for-the-tie/"&gt;Survival is a huge milestone but it isn't the end goal&lt;/a&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;You have no money and aren't interested in loans.&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;If you have no money, this is probably your first venture. I strongly feel loans are a bad idea for your first venture, but others have different opinions. &lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.thecodefactory.ca/blog/2008/12/12/rocket-fuel-or-gasoline/"&gt;Cash is a great accelerator&lt;/a&gt; - once you've launched your first business you'll probably have a need for speed and will either have cash or be more open to debt/equity financing because you'll have already learned what you have to learn in organic growth. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;You're starting a software company.&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;It is possible to start a &lt;a href="http://blog.lavablast.com/post/2008/03/11/Software-Startup-Lessons-(Part-1)-The-Basics.aspx"&gt;software startup with limited cash&lt;/a&gt;. You've picked a good industry. If you wanted to become a dairy farmer, you would need a massive initial investment. However, for a software startup, your investment will be time writing code - not acquiring assets. &lt;/li&gt; &lt;/ul&gt;  &lt;h1&gt;Tip 1) Sell to the right group&lt;/h1&gt;  &lt;p&gt;&lt;strong&gt;Since this is your first business and you have no money, you need to establish a consulting sideline selling to businesses that will give you a good return for your time&lt;/strong&gt; (even if you're building a product for individuals). You won't be able to pay your bills selling $20 licenses to individuals in the early stage. We recommend selling customized versions of something that will help you grow your core product, as long as you can keep the intellectual property. &lt;a href="http://blog.lavablast.com/post/2009/04/06/Software-Startup-Lessons-(Part-5)-ndash3b-Being-a-software-startup-in-a-recession.aspx"&gt;Read more about this strategy in our previous post&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Everyone values the dollar differently. The earlier stage you are, the harder it is to define appropriate pricing given your credibility level and you don't give the same value to each dollar as your customers. As you grow, you’ll find your sweet spot and will be able to focus on your consulting clients that are right for you. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Eventually, you should aim at moving out of consulting, as it doesn't scale.&lt;/strong&gt;&lt;/p&gt;  &lt;h4&gt;&lt;/h4&gt;  &lt;h1&gt;Tip 2) Minimize your expenses&lt;/h1&gt;  &lt;p&gt;&lt;a href="http://www.flickr.com/photos/etremblay/4024267390/"&gt;&lt;img style="border-right-width: 0px; margin: 10px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="No, this isn&amp;#39;t where we started LavaBlast!" border="0" alt="No, this isn&amp;#39;t where we started LavaBlast!" align="right" src="http://blog.lavablast.com/image.axd?picture=shack.jpg" width="364" height="244" /&gt;&lt;/a&gt; Assuming you have no money, it's important that you only spend when necessary. At a high level, you &lt;a href="http://blog.lavablast.com/post/2009/04/20/Software-Startup-Lessons-(Part-7)-ndash3b-Versatility.aspx"&gt;need to be versatile&lt;/a&gt; and be able to do as much as you can on your own. Later, you'll be able to delegate but not in the early days. Of course, know your limits and get &lt;a href="http://twitter.com/startupcfo"&gt;pros&lt;/a&gt; to do things that are impossible for you to do properly.&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;Don't hire an accountant to prepare invoices for you.&lt;/strong&gt; Learn how to use accounting software and do it yourself. Only hire the accountant for an annual review or for &lt;a href="http://www.startupcfo.ca/2009/10/choosing-ca-cpa-firm.html"&gt;real accounting work&lt;/a&gt;. Once you know how, it will take you a few seconds or a few minutes to do the most common tasks - you won't be paying someone four hours of work at a high hourly rate.&amp;#160; &lt;/li&gt; &lt;/ul&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;Don't hire a law firm to review a simple non-disclosure agreement sent to you by a customer. &lt;/strong&gt;Learn to read legal text on your own. Only hire a &lt;a href="http://twitter.com/lwstartuplaw"&gt;law firm&lt;/a&gt; when you've got something important to prepare or review. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;There are plenty of examples of ways not to spend money when you're just starting out and have none of your own. It's time to learn things on your own.&lt;/p&gt;  &lt;h3&gt;Be smart about the commercial bank account you choose&lt;/h3&gt;  &lt;p&gt;I've dealt with a few different banks over the years. If you're a tiny business, it is good to know a few simple facts and comparison points.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Get a business account with a variable monthly fee&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Don't bite when they offer you a $50/month fixed rate. You won't have enough transactions to make it worthwhile to upgrade. When you reach that point, switch to the fixed rate plan that is a best fit for your business. You can easily save $480 per year. &lt;/li&gt;    &lt;li&gt;Some Canadian examples: &lt;a href="http://www.desjardins.com"&gt;Desjardins&lt;/a&gt;: $7/month, &lt;a href="http://www.tdcanadatrust.com"&gt;TD Canada Trust&lt;/a&gt;: $12.50/month, &lt;a href="http://www.rbcroyalbank.com"&gt;Royal Bank of Canada&lt;/a&gt;: $6/month &lt;/li&gt;    &lt;li&gt;Some variable plans charge transactions on top of the minimum monthly fee. Do your homework. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;Know the minimum balance you need to get it for free&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Save up to $150 per year by making sure you don't go under the minimum. &lt;/li&gt;    &lt;li&gt;Some Canadian examples: &lt;a href="http://www.desjardins.com"&gt;Desjardins&lt;/a&gt;: $2000, &lt;a href="http://www.tdcanadatrust.com"&gt;TD Canada Trust&lt;/a&gt;: $8000, &lt;a href="http://www.rbcroyalbank.com"&gt;Royal Bank of Canada&lt;/a&gt;: n/a &lt;strong&gt;&lt;/strong&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;ul&gt;&lt;strong&gt;Get an ING Direct Savings Account&lt;/strong&gt;     &lt;li&gt;Some customers may pay you in advance or you may get grant money. Bottom line: you may end up with cash that you can't spend for a few months to a year. (Actually, you can spend it if you know more will be coming in - depends on your management style.) If you do have it in the account, earn interest on it. &lt;/li&gt;    &lt;li&gt;Business accounts often don't give interest. If they do, the interest is horribly low if you don't lock it in. (Not paying service fees is often more than the amount you'd earn in interest anyways). &lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.ingdirect.ca/referafriend/"&gt;ING Direct's&lt;/a&gt; account is free. They have the best rates I've seen for low amounts that can be withdrawn at any time. &lt;/li&gt;    &lt;li&gt;Best of all, they have a &lt;a href="http://www.ingdirect.ca/referafriend/"&gt;referral program&lt;/a&gt;. Both the new member and the referrer earn $25. In today's market, this could easily end up being worth more than the interest you'll generate in your first year. &lt;strong&gt;Our orange key is 33514316S1&lt;/strong&gt; – go ahead, signup (personal or business) and you’ll &lt;strong&gt;help support us and &lt;/strong&gt;&lt;a href="http://www.ingdirect.ca/referafriend/"&gt;&lt;strong&gt;receive $25&lt;/strong&gt;&lt;/a&gt;! :) &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;Don't get a commercial credit card for your purchases&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Unlike personal cards, they're not free and most don't have any rewards programs. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;Do you really need to accept credit cards?&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;It is a good fit for some users or services, but know the costs. If you have few transactions but most of them are high value, you're better off with a wire transfer. &lt;/li&gt;    &lt;li&gt;Some banks charge you more for wire transfers than others. &lt;/li&gt;    &lt;li&gt;Remember that cheques are slow - you don't have access to the funds are week. &lt;/li&gt;    &lt;li&gt;You'll be paying $20-50 per month plus 2-3% per transaction. This quickly amounts to several thousand dollars. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;Will you be dealing with multiple currencies?&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;We're a Canadian company but we have lots of clients in the US and in Europe. In the very early days, we chose not to open two separate bank accounts (one in each currency) because of the associated ongoing operating costs and increased accounting complexity. &lt;/li&gt;    &lt;li&gt;Banks all have different exchange rates. However, I've found one bank consistently gives us a significantly worse rate when receiving transfers in another currency. A few percentage points makes a huge difference as the size of the payments increases. &lt;/li&gt;    &lt;li&gt;The larger your conversion, the better your rates. Talk to a specialist like &lt;a href="http://twitter.com/JamesonBankTrav"&gt;@JamesonBankTrav&lt;/a&gt;. &lt;/li&gt; &lt;/ul&gt;  &lt;h3&gt;Minimize your telecommunication fees&lt;/h3&gt;  &lt;p&gt;&lt;strong&gt;Don't get a commercial telephone line via large companies&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;You'll pay much more than needed. Investigate Voice Over IP solutions such as &lt;a href="http://www.skype.com"&gt;Skype&lt;/a&gt;. You can get your own telephone number and free long distance in Canada + USA for an annual fee of $60. This service saves you hundreds per year. Don't get a fax unless your customers nag you for your fax phone number often enough. If you need one, look at online services such as &lt;a href="http://myfax.com"&gt;myfax.com&lt;/a&gt; which deliver faxes by email and give you toll-free fax numbers for less than what you'd pay to get a separate telephone line in your office for the fax, without the clutter of a deprecated device.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Don't sign-up for a massive cell phone plan if you've got empty pockets&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;Depending on what you do with your phone, you can save upwards of a thousand dollars a year by downsizing to a prepaid plan. Smartphones are great, but depending on your situation, it might be a wise choice to minimize those expenses. Let's hope you're not locked into a crazy-expensive three year plan! In the end, this is a personal decision which depends on your personality; once you've tasted a smartphone you may be unable to go back. Just keep in mind you might be paying much more for your cell phone than the much faster Internet connection you use all day.&lt;/p&gt;  &lt;h3&gt;Minimize your rent&lt;/h3&gt;  &lt;p&gt;&lt;strong&gt;Use a co-working facility&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;One tip often given to people starting their own company is to avoid renting office space too early in the process. Instead, work from home or from a more affordable &lt;a href="http://www.thecodefactory.ca"&gt;co-working location&lt;/a&gt;. Not only do co-working locations reduce costs, they help you build your business because of the contacts you can make there. Once you’re read, upgrade to &lt;a href="http://www.thecodefactory.ca"&gt;shared office space&lt;/a&gt;. &lt;/p&gt;  &lt;h3&gt;Don't minimize everything&lt;/h3&gt;  &lt;p&gt;In addition to being able to exchange services with other companies to cut costs, there are a few places where you can't afford to cut costs.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Your Image&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;One thing you don't want to be cheap on is branding. Your image is everything - quality needs to be high. Get nice business cards created by pros. Don't do your own web design if that's not something you specialize in. Your product will look amateurish and you'll lose sales. There are tons of affordable graphic designers out there: find one and have something nice created. Use online marketplaces such as &lt;a href="http://www.99designs.com"&gt;99designs&lt;/a&gt;. Since you're still a software expert, however, you should know enough HTML and search engine optimization techniques to be able to maintain your website. If you're a horrible writer, have someone review your content. This basically boils down to knowing your limits; there are some things you won't be good enough at even if you try.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Hardware &amp;amp; work area&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;We agree with &lt;a href="http://www.joelonsoftware.com/"&gt;Joel Spolsky's&lt;/a&gt; view that you should buy the &lt;a href="http://blog.lavablast.com/post/2009/03/30/Software-Startup-Lessons-(Part-4)-ndash3b-Year-Two.aspx"&gt;best computer hardware and computer chair you can find&lt;/a&gt;. These are your primary tools and they are relatively inexpensive compared to your salary, even when you have very low revenue. One investment that is definitely worth it is a second monitor as it tremendously increases your productivity.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Your health&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;As much as your work environment is important, you should also value your health. Even if you're living on a very tight budget, don't eat hot dogs all day. Proper nutrition and good sleep cycles keep you in good health and makes you more productive. You should not be falling asleep in the afternoon. &lt;strong&gt;Starting your business is a marathon, not a sprint.&lt;/strong&gt; Make sure your lifestyle is well adapted for a marathon.&lt;/p&gt;  &lt;h1&gt;Tip 3) Leverage your money&lt;/h1&gt;  &lt;p&gt;We've already covered this part in &lt;a href="http://blog.lavablast.com/post/2009/03/30/Software-Startup-Lessons-(Part-4)-ndash3b-Year-Two.aspx"&gt;a previous blog post&lt;/a&gt;. Know what government funding opportunities are out there. Some require matching contributions. Some are based on your expenditures. Look around for these opportunities but mostly talk to other people to know what's out there and what's worthwhile.&lt;/p&gt;  &lt;h1&gt;Tip 4) Cash flow projections for dummies&lt;/h1&gt;  &lt;p&gt;You should always keep an eye on your cash flow, not just your revenue. I've created a &lt;a href="http://blog.lavablast.com/uploads/CashFlowProjections_Template.xlsx"&gt;very simple Excel spreadsheet to help with our cash flow projections&lt;/a&gt;. This one is simply a template with some random numbers in there. The one we use internally is a bit more complicated as it includes things such as currency exchange rates, taxes, etc. Build it however you like, but I've found that the two most important elements in there are:&lt;/p&gt;  &lt;h3&gt;1) Past Sales versus Projected Sales&lt;/h3&gt;  &lt;p&gt;What are my known sales (recurring revenue) versus what serious leads do I have in the pipeline. Being conservative, I base my business decisions on my past sales not my projected sales because I've learned that projected sales are often postponed. We have long sales cycles that culminate with a large sale which has a big impact on that month's revenue. Separating known sales from projected sales is of critical importance because of this because we either make the sale or get zero revenue from that customer in that month. If you're selling lots of lower value items (subscriptions to your service, for example), each individual customer has less impact on your total monthly revenue.&lt;/p&gt;  &lt;h3&gt;2) Runway&lt;/h3&gt;  &lt;p&gt;Given our current burn rate, when will we run out of cash if none of the sales in the pipeline are realized. This is useful to help you decide if you can hire and/or if you can give yourself a raise. It can also make you realize you're heading towards a problem and you need to correct the situation as soon as possible.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blog.lavablast.com/uploads/CashFlowProjections_Template.xlsx"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="cashflow" border="0" alt="cashflow" src="http://blog.lavablast.com/image.axd?picture=cashflow.png" width="550" height="416" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;It would be nice to have a simple, open source, application that helps business owners track their cash flow projections in this fashion. You could go overboard and integrate it with accounting software, but I think it's nice when it's simple.&lt;/p&gt;  &lt;h1&gt;Conclusion&lt;/h1&gt;  &lt;p&gt;&lt;a href="http://www.flickr.com/photos/etremblay/3985612718/sizes/l/"&gt;&lt;img style="border-right-width: 0px; margin: 10px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="The path ends up being longer than expected" border="0" alt="The path ends up being longer than expected" align="right" src="http://blog.lavablast.com/image.axd?picture=path2.jpg" width="364" height="244" /&gt;&lt;/a&gt; When you start your first company, and you have no cash on hand, you need to focus on making money and keeping the little money you have. &lt;strong&gt;Survival is a major milestone, but remember that it isn't the end goal.&lt;/strong&gt; You'll learn tons of things along the way, and once you do leave the very early stages, you'll need to manage your cash flow properly. Later on in life, you'll probably start another business - this time you hopefully won't be as strapped for cash - and you'll be able to speed up the whole process.&lt;/p&gt;  &lt;p&gt;I'm not sure what is harder between:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;A) Going from nothing to survival &lt;/li&gt;    &lt;li&gt;B) Going from survival to success &lt;/li&gt; &lt;/ul&gt;  &lt;ul&gt;I do know, however, that &lt;strong&gt;going from nothing to survival appears a lot easier if you have cash to start off with or if you've done it before.&lt;/strong&gt; Since success is in the eye of the beholder, it all depends on what you want to achieve.&lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/SPt_wD5tO3Y" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/SPt_wD5tO3Y/post.aspx</link>
      <author>jkealey</author>
      <comments>http://blog.lavablast.com/post/2009/11/06/Penniless-Startup-Founders.aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=0b8086b1-9283-4f48-91ef-58260c1bf103</guid>
      <pubDate>Fri, 06 Nov 2009 09:02:00 -0500</pubDate>
      <category>Business</category>
      <category>Software</category>
      <category>Ideas</category>
      <dc:publisher>jkealey</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=0b8086b1-9283-4f48-91ef-58260c1bf103</pingback:target>
      <slash:comments>10</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=0b8086b1-9283-4f48-91ef-58260c1bf103</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2009/11/06/Penniless-Startup-Founders.aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=0b8086b1-9283-4f48-91ef-58260c1bf103</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=0b8086b1-9283-4f48-91ef-58260c1bf103</feedburner:origLink></item>
    <item>
      <title>Sweet Mischief Cakes</title>
      <description>&lt;p&gt;
Although we haven&amp;rsquo;t been very vocal lately, we have lots going on here at &lt;a href="http://www.lavablast.com"&gt;LavaBlast&lt;/a&gt; and hope to write up a few technical posts and make a few announcements this coming fall. In the meantime, don&amp;rsquo;t be shy and connect with two of LavaBlast&amp;rsquo;s co-founders on &lt;a href="http://www.twitter.com/lavablast"&gt;Twitter&lt;/a&gt; (&lt;a href="http://twitter.com/jkealey"&gt;@jkealey&lt;/a&gt;, &lt;a href="http://twitter.com/etiennet"&gt;@etiennet&lt;/a&gt;). Alternatively, come meet us in person at this week&amp;rsquo;s &lt;a href="http://www.startupottawa.com/?p=1622"&gt;Startup Ottawa Drinks&lt;/a&gt;. 
&lt;/p&gt;
&lt;p&gt;
Last Friday, we gathered to celebrate Jason&amp;rsquo;s birthday.&amp;nbsp; As co-founder and president of LavaBlast, &lt;a href="http://blog.lavablast.com/author/JKealey.aspx"&gt;Jason&lt;/a&gt; has helped build this software start-up from the ground up over the past two and a half years; there&amp;rsquo;s no doubt that LavaBlast is a big part of his life!&amp;nbsp; Our friend &lt;a href="http://jpdaigle.blogspot.com/"&gt;Jean-Philippe&lt;/a&gt; had the great idea to buy a custom cake for LavaBlast&amp;rsquo;s leader!&amp;nbsp; Here are some pictures:
&lt;/p&gt;
&lt;table border="0" cellspacing="0" cellpadding="2" width="600"&gt;
	&lt;tbody&gt;
		&lt;tr&gt;
			&lt;td width="200" valign="top"&gt;&lt;a href="http://www.flickr.com/photos/etremblay/3962664855/sizes/l/"&gt;&lt;img style="border: 0px none ; display: inline" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/JasonsBirthday/6E9CC168/IMG_4097.jpg" border="0" alt="IMG_4097" title="IMG_4097" width="402" height="274" /&gt;&lt;/a&gt; &lt;/td&gt;        
			&lt;td width="200" valign="top"&gt;&lt;a href="http://www.flickr.com/photos/etremblay/3962666607/sizes/l/"&gt;&lt;img style="border: 0px none ; margin: 0px; display: inline" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/JasonsBirthday/0249AAFD/IMG_4136.jpg" border="0" alt="IMG_4136" title="IMG_4136" width="402" height="275" /&gt;&lt;/a&gt;&lt;/td&gt;        
			&lt;td width="200" valign="top"&gt;&lt;a href="http://www.flickr.com/photos/etremblay/3962667331/sizes/l/"&gt;&lt;img style="border: 0px none ; margin: 0px 0px 10px 10px; display: inline" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/JasonsBirthday/739EFF17/IMG_4141Edit2.jpg" border="0" alt="IMG_4141-Edit-2" title="IMG_4141-Edit-2" width="276" height="276" /&gt;&lt;/a&gt;&lt;/td&gt;     
		&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;
&amp;nbsp;
&lt;/p&gt;
&lt;p&gt;
As you can see, even Calis (Jason&amp;rsquo;s dog) wanted a piece of the cake!&amp;nbsp; In additional to be the only such cake in the universe, it was of high quality and tasted great!&amp;nbsp; Since it was a chocolate cake, it was a lot of work to have a white icing coating it.&amp;nbsp; I would recommend &lt;a href="http://sweetmischiefcakes.com/default.html"&gt;Sweet Mischief Cakes&lt;/a&gt; (another &lt;a href="http://www.startupottawa.com"&gt;Ottawa startup&lt;/a&gt;!) to anyone living in the National Capital Region.&amp;nbsp; Cakes are hand made and you can choose pretty much any design you want on it.
&lt;/p&gt;
&lt;p&gt;
Launching a startup is a &lt;a href="http://ricksegal.typepad.com/pmv/2009/09/eyodf-part-8---how-to-hold-down-10-jobs-simultaneously.html"&gt;tough job&lt;/a&gt; but, once in a while, you need to sit back and enjoy life&amp;rsquo;s simple pleasures. 
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/J5QqXsC1Y8M" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/J5QqXsC1Y8M/post.aspx</link>
      <author>EtienneT</author>
      <comments>http://blog.lavablast.com/post/2009/09/28/Sweet-Mischief-Cakes.aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=718eb5b4-5dbe-43fa-b94c-a7ac64d657b3</guid>
      <pubDate>Mon, 28 Sep 2009 13:33:00 -0500</pubDate>
      <category>News</category>
      <dc:publisher>EtienneT</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=718eb5b4-5dbe-43fa-b94c-a7ac64d657b3</pingback:target>
      <slash:comments>1</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=718eb5b4-5dbe-43fa-b94c-a7ac64d657b3</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2009/09/28/Sweet-Mischief-Cakes.aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=718eb5b4-5dbe-43fa-b94c-a7ac64d657b3</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=718eb5b4-5dbe-43fa-b94c-a7ac64d657b3</feedburner:origLink></item>
    <item>
      <title>Gotcha: gzip compression in SOAP message calls</title>
      <description>&lt;p&gt;
Some of our software systems here at &lt;a href="http://www.lavablast.com"&gt;LavaBlast&lt;/a&gt; exchange &amp;ldquo;large&amp;rdquo; data sets (only a few megabytes) in order to synchronize data between the systems. The data being exchanged is simple XML and we assumed that the SOAP calls were compressed by the built-in HTTP compression in the web server. However, checking the HTTP headers on the server when a call was made, we noticed that the &lt;em&gt;Accept-Encoding &lt;/em&gt;header was missing. (&lt;em&gt;HttpContext.Current.Request.Headers[&amp;quot;Accept-Encoding&amp;quot;]&lt;/em&gt; did not exist). 
&lt;/p&gt;
&lt;p&gt;
Surprised, we turn to Google and immediately found a reference to the &lt;em&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/system.web.services.protocols.httpwebclientprotocol.enabledecompression.aspx"&gt;EnableDecompression&lt;/a&gt;&lt;/em&gt; property of the SOAP client. By setting it to true, we ensured our SOAP client was requesting gzip compression when making calls. 
&lt;/p&gt;
&lt;div class="csharpcode"&gt;
&lt;pre class="alt"&gt;
LavaBlastServer.Inventory centralServer = &lt;span class="kwrd"&gt;new&lt;/span&gt; LavaBlastServer.Inventory();
&lt;/pre&gt;
&lt;pre&gt;
centralServer.EnableDecompression = &lt;span class="kwrd"&gt;true&lt;/span&gt;;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;
There is not much more to it. However, if you&amp;rsquo;re unaware that this setting is required, you may have clients running in the wild that don&amp;rsquo;t use gzip compression but could benefit from it. 
&lt;/p&gt;
&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2009%2f08%2fGotcha-gzip-compression-in-SOAP-message-calls.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2009%2f08%2fGotcha-gzip-compression-in-SOAP-message-calls.aspx&amp;amp;border=660000&amp;amp;fgcolor=660000&amp;amp;bgcolor=FF9900&amp;amp;cbgcolor=FFFF00" border="0" alt="kick it on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/PWIhcy2XmI4" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/PWIhcy2XmI4/post.aspx</link>
      <author>JKealey</author>
      <comments>http://blog.lavablast.com/post/2009/08/20/Gotcha-gzip-compression-in-SOAP-message-calls.aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=4548f5a2-4739-4e1d-a447-529a4513e013</guid>
      <pubDate>Thu, 20 Aug 2009 10:30:00 -0500</pubDate>
      <category>Software</category>
      <dc:publisher>JKealey</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=4548f5a2-4739-4e1d-a447-529a4513e013</pingback:target>
      <slash:comments>1</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=4548f5a2-4739-4e1d-a447-529a4513e013</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2009/08/20/Gotcha-gzip-compression-in-SOAP-message-calls.aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=4548f5a2-4739-4e1d-a447-529a4513e013</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=4548f5a2-4739-4e1d-a447-529a4513e013</feedburner:origLink></item>
    <item>
      <title>Multi-level contextual menus in Eclipse/GEF</title>
      <description>&lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;h2&gt;Scenario: I want my ContextMenuProvider to have multiple levels&lt;/h2&gt;  &lt;p&gt;Imagine you already have a ContextMenuProvider setup in your GEF editor but you would like to have multiple levels of actions, grouping elements together. This is one of the elements we recently had to accomplish in &lt;a href="http://jucmnav.softwareengineering.ca/twiki/bin/view/ProjetSEG/WebHome"&gt;jUCMNav&lt;/a&gt; and since the we couldn’t easily find sample code on Google, we thought it would be nice to share this code with you.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;a href="http://jucmnav.softwareengineering.ca/twiki/bin/view/ProjetSEG/WebHome"&gt;&lt;img style="border-right-width: 0px; margin: 10px auto; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="multilevel_contextmenu" border="0" alt="multilevel_contextmenu" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/MultilevelcontextualmenusinEclipseGEF/25B5C85D/multilevel_contextmenu.png" width="244" height="230" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h3&gt;Step 1) Code for the sub menu container&lt;/h3&gt;  &lt;div class="csharpcode"&gt;   &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;package seg.jUCMNav.actions;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;import org.eclipse.jface.action.Action;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;import org.eclipse.jface.action.IAction;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;import org.eclipse.jface.action.IMenuCreator;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;import org.eclipse.jface.resource.ImageDescriptor;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;import org.eclipse.swt.SWT;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;import org.eclipse.swt.events.SelectionEvent;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;import org.eclipse.swt.events.SelectionListener;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;import org.eclipse.swt.widgets.Control;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;import org.eclipse.swt.widgets.Menu;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;import org.eclipse.swt.widgets.MenuItem;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;&lt;span class="rem"&gt;/**&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;&lt;span class="rem"&gt; * This action contains other actions and helps create another level of&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;&lt;span class="rem"&gt; * contextual menus.&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;&lt;span class="rem"&gt; * &lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;&lt;span class="rem"&gt; * @author jkealey&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;&lt;span class="rem"&gt; * &lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;&lt;span class="rem"&gt; */&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; SubmenuAction extends Action implements SelectionListener&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;{&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;    &lt;span class="rem"&gt;// / Who to inform when this action is fired (meaning display the submenu)&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; SelectionListener actionInstance;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;    &lt;span class="rem"&gt;// the list of actions that are contained within this action&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; IAction[] actions;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  29:  &lt;/span&gt;    &lt;span class="rem"&gt;// should we hide the disabled ones (if not, they will appear as grayed out)&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  30:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; boolean hideDisabled;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  31:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  32:  &lt;/span&gt;    &lt;span class="rem"&gt;/***&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  33:  &lt;/span&gt;&lt;span class="rem"&gt;     * Create a submenu.&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  34:  &lt;/span&gt;&lt;span class="rem"&gt;     * &lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  35:  &lt;/span&gt;&lt;span class="rem"&gt;     * @param subactions&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  36:  &lt;/span&gt;&lt;span class="rem"&gt;     *            the actions that are contained within&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  37:  &lt;/span&gt;&lt;span class="rem"&gt;     * @param text&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  38:  &lt;/span&gt;&lt;span class="rem"&gt;     *            the container's textual label&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  39:  &lt;/span&gt;&lt;span class="rem"&gt;     * @param toolTip&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  40:  &lt;/span&gt;&lt;span class="rem"&gt;     *            the container's tooltip&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  41:  &lt;/span&gt;&lt;span class="rem"&gt;     * @param descriptor&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  42:  &lt;/span&gt;&lt;span class="rem"&gt;     *            the container's image descriptor&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  43:  &lt;/span&gt;&lt;span class="rem"&gt;     * @param hideDisabledActions&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  44:  &lt;/span&gt;&lt;span class="rem"&gt;     *            should we hide the disabled ones (if not, they will appear as&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  45:  &lt;/span&gt;&lt;span class="rem"&gt;     *            grayed out)&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  46:  &lt;/span&gt;&lt;span class="rem"&gt;     */&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  47:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; SubmenuAction(IAction[] subactions, String text, String toolTip, ImageDescriptor descriptor, boolean hideDisabledActions)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  48:  &lt;/span&gt;    {&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  49:  &lt;/span&gt;        &lt;span class="rem"&gt;// indicate that this is a secondary fly-out menu.&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  50:  &lt;/span&gt;        super(&lt;span class="str"&gt;&amp;quot;&amp;quot;&lt;/span&gt;, IAction.AS_DROP_DOWN_MENU);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  51:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  52:  &lt;/span&gt;        &lt;span class="kwrd"&gt;this&lt;/span&gt;.actionInstance = &lt;span class="kwrd"&gt;this&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  53:  &lt;/span&gt;        &lt;span class="kwrd"&gt;this&lt;/span&gt;.actions = subactions;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  54:  &lt;/span&gt;        &lt;span class="kwrd"&gt;this&lt;/span&gt;.hideDisabled = hideDisabledActions;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  55:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  56:  &lt;/span&gt;        setText(text);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  57:  &lt;/span&gt;        setToolTipText(toolTip);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  58:  &lt;/span&gt;        setImageDescriptor(descriptor);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  59:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  60:  &lt;/span&gt;        &lt;span class="rem"&gt;// the secondayr menu logic&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  61:  &lt;/span&gt;        setMenuCreator(&lt;span class="kwrd"&gt;new&lt;/span&gt; IMenuCreator()&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  62:  &lt;/span&gt;        {&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  63:  &lt;/span&gt;            &lt;span class="kwrd"&gt;public&lt;/span&gt; Menu getMenu(Control parent)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  64:  &lt;/span&gt;            {&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  65:  &lt;/span&gt;                &lt;span class="rem"&gt;// this would be used outside of a menu. not useful for us.&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  66:  &lt;/span&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  67:  &lt;/span&gt;            }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  68:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  69:  &lt;/span&gt;            &lt;span class="kwrd"&gt;public&lt;/span&gt; Menu getMenu(Menu parent)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  70:  &lt;/span&gt;            {&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  71:  &lt;/span&gt;                &lt;span class="rem"&gt;// create a submenu&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  72:  &lt;/span&gt;                Menu menu = &lt;span class="kwrd"&gt;new&lt;/span&gt; Menu(parent);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  73:  &lt;/span&gt;                &lt;span class="rem"&gt;// fill it with our actions&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  74:  &lt;/span&gt;                &lt;span class="kwrd"&gt;for&lt;/span&gt; (&lt;span class="kwrd"&gt;int&lt;/span&gt; i = 0; i &amp;lt; actions.length; i++)&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  75:  &lt;/span&gt;                {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  76:  &lt;/span&gt;                    &lt;span class="rem"&gt;// skip the disabled ones if necessary (or null actions)&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  77:  &lt;/span&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (actions[i] == &lt;span class="kwrd"&gt;null&lt;/span&gt; || !actions[i].isEnabled() &amp;amp;&amp;amp; hideDisabled)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  78:  &lt;/span&gt;                        &lt;span class="kwrd"&gt;continue&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  79:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  80:  &lt;/span&gt;                    &lt;span class="rem"&gt;// create the submenu item&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  81:  &lt;/span&gt;                    MenuItem item = &lt;span class="kwrd"&gt;new&lt;/span&gt; MenuItem(menu, SWT.NONE);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  82:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  83:  &lt;/span&gt;                    &lt;span class="rem"&gt;// memorize the index&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  84:  &lt;/span&gt;                    item.setData(&lt;span class="kwrd"&gt;new&lt;/span&gt; Integer(i));&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  85:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  86:  &lt;/span&gt;                    &lt;span class="rem"&gt;// identify it&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  87:  &lt;/span&gt;                    item.setText(actions[i].getText());&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  88:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  89:  &lt;/span&gt;                    &lt;span class="rem"&gt;// create its image&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  90:  &lt;/span&gt;                    &lt;span class="kwrd"&gt;if&lt;/span&gt; (actions[i].getImageDescriptor() != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  91:  &lt;/span&gt;                        item.setImage(actions[i].getImageDescriptor().createImage());&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  92:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  93:  &lt;/span&gt;                    &lt;span class="rem"&gt;// inform us when something is selected.&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  94:  &lt;/span&gt;                    item.addSelectionListener(actionInstance);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  95:  &lt;/span&gt;                }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  96:  &lt;/span&gt;                &lt;span class="kwrd"&gt;return&lt;/span&gt; menu;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  97:  &lt;/span&gt;            }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  98:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  99:  &lt;/span&gt;            &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; dispose()&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 100:  &lt;/span&gt;            {&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 101:  &lt;/span&gt;            }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 102:  &lt;/span&gt;        });&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 103:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 104:  &lt;/span&gt;    }&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 105:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 106:  &lt;/span&gt;    &lt;span class="rem"&gt;/**&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 107:  &lt;/span&gt;&lt;span class="rem"&gt;     * Returns how many items are enabled in the flyout. Useful to hide the&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 108:  &lt;/span&gt;&lt;span class="rem"&gt;     * submenu when none are enabled.&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 109:  &lt;/span&gt;&lt;span class="rem"&gt;     * &lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 110:  &lt;/span&gt;&lt;span class="rem"&gt;     * @return the number of currently enabled menu items.&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 111:  &lt;/span&gt;&lt;span class="rem"&gt;     */&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 112:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt; getActiveOperationCount()&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 113:  &lt;/span&gt;    {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 114:  &lt;/span&gt;        &lt;span class="kwrd"&gt;int&lt;/span&gt; operationCount = 0;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 115:  &lt;/span&gt;        &lt;span class="kwrd"&gt;for&lt;/span&gt; (&lt;span class="kwrd"&gt;int&lt;/span&gt; i = 0; i &amp;lt; actions.length; i++)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 116:  &lt;/span&gt;            operationCount += actions[i] != &lt;span class="kwrd"&gt;null&lt;/span&gt; &amp;amp;&amp;amp; actions[i].isEnabled() ? 1 : 0;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 117:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 118:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; operationCount;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 119:  &lt;/span&gt;    }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 120:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 121:  &lt;/span&gt;    &lt;span class="rem"&gt;/**&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 122:  &lt;/span&gt;&lt;span class="rem"&gt;     * Runs the default action&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 123:  &lt;/span&gt;&lt;span class="rem"&gt;     */&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 124:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; run()&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 125:  &lt;/span&gt;    {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 126:  &lt;/span&gt;        actions[0].run();&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 127:  &lt;/span&gt;    }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 128:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 129:  &lt;/span&gt;    &lt;span class="rem"&gt;/**&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 130:  &lt;/span&gt;&lt;span class="rem"&gt;     * Runs the default action&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 131:  &lt;/span&gt;&lt;span class="rem"&gt;     */&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 132:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; widgetDefaultSelected(SelectionEvent e)&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 133:  &lt;/span&gt;    {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 134:  &lt;/span&gt;        actions[0].run();&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 135:  &lt;/span&gt;    }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 136:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 137:  &lt;/span&gt;    &lt;span class="rem"&gt;/**&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 138:  &lt;/span&gt;&lt;span class="rem"&gt;     * Called when an item in the drop-down menu is selected. Runs the&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 139:  &lt;/span&gt;&lt;span class="rem"&gt;     * associated run() method&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 140:  &lt;/span&gt;&lt;span class="rem"&gt;     */&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 141:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; widgetSelected(SelectionEvent e)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 142:  &lt;/span&gt;    {&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 143:  &lt;/span&gt;        &lt;span class="rem"&gt;// get the index from the data and run that action.&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 144:  &lt;/span&gt;        actions[((Integer) (((MenuItem) (e.getSource())).getData())).intValue()].run();&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 145:  &lt;/span&gt;    }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt; 146:  &lt;/span&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;Step 2) Setup your GEF ContextMenuProvider&lt;/h3&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; UrnContextMenuProvider extends ContextMenuProvider {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; ActionRegistry actionRegistry;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;    &lt;span class="rem"&gt;/**&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;&lt;span class="rem"&gt;     * @param viewer&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;&lt;span class="rem"&gt;     * @param registry&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;&lt;span class="rem"&gt;     *            has to be passed in case we don't want to use the action registry used in the viewer. [is this bad coding?]&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;&lt;span class="rem"&gt;     */&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; UrnContextMenuProvider(EditPartViewer viewer, ActionRegistry registry) {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;        super(viewer);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;        setActionRegistry(registry);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;    }&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;    &lt;span class="rem"&gt;/**&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;&lt;span class="rem"&gt;     * &lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;&lt;span class="rem"&gt;     * @return the action registry used by the context menu provider.&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;&lt;span class="rem"&gt;     */&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; ActionRegistry getActionRegistry() {&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; actionRegistry;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;    }&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;    &lt;span class="rem"&gt;/**&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;&lt;span class="rem"&gt;     * &lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;&lt;span class="rem"&gt;     * @param registry&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;&lt;span class="rem"&gt;     *            the action registry used by the context menu provider.&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;&lt;span class="rem"&gt;     */&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; setActionRegistry(ActionRegistry registry) {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;        actionRegistry = registry;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  29:  &lt;/span&gt;    }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  30:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  31:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  32:  &lt;/span&gt;    &lt;span class="rem"&gt;/**&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  33:  &lt;/span&gt;&lt;span class="rem"&gt;     * Looks up a set of actions in the action registry. If they are enabled, adds them to the correct groups.&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  34:  &lt;/span&gt;&lt;span class="rem"&gt;     */&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  35:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; buildContextMenu(IMenuManager manager) {&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  36:  &lt;/span&gt;        GEFActionConstants.addStandardActionGroups(manager);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  37:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  38:  &lt;/span&gt;    &lt;span class="rem"&gt;// regular action&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  39:  &lt;/span&gt;    IAction action = getActionRegistry().getAction(AddLabelAction.ADDLABEL);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  40:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (action.isEnabled())&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  41:  &lt;/span&gt;            manager.appendToGroup(GEFActionConstants.GROUP_REST, action);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  42:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  43:  &lt;/span&gt;    &lt;span class="rem"&gt;// compound action&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  44:  &lt;/span&gt;        IAction[] actions = &lt;span class="kwrd"&gt;new&lt;/span&gt; IAction[13];&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  45:  &lt;/span&gt;        actions[0] = getActionRegistry().getAction(AddOrForkAction.ADDORFORK);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  46:  &lt;/span&gt;        actions[1] = getActionRegistry().getAction(AddAndForkAction.ADDANDFORK);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  47:  &lt;/span&gt;        actions[2] = getActionRegistry().getAction(AddOrJoinAction.ADDORJOIN);&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  48:  &lt;/span&gt;        actions[3] = getActionRegistry().getAction(AddAndJoinAction.ADDANDJOIN);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  49:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  50:  &lt;/span&gt;        SubmenuAction submenu = &lt;span class="kwrd"&gt;new&lt;/span&gt; SubmenuAction(actions, &lt;span class="str"&gt;&amp;quot;Path Operations&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;Path operations&amp;quot;&lt;/span&gt;, actions[0].getImageDescriptor(), &lt;span class="kwrd"&gt;true&lt;/span&gt;); &lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  51:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (submenu.getActiveOperationCount()&amp;gt;0)&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  52:  &lt;/span&gt;            manager.appendToGroup(GEFActionConstants.GROUP_REST, submenu);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  53:  &lt;/span&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  54:  &lt;/span&gt;    &lt;span class="rem"&gt;// ... add other actions ... &lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  55:  &lt;/span&gt;   }&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="lnum"&gt;  56:  &lt;/span&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;That’s all there is to it! &lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/ckJ2r4lG0Ls" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/ckJ2r4lG0Ls/post.aspx</link>
      <author>jkealey</author>
      <comments>http://blog.lavablast.com/post/2009/06/22/Multi-level-contextual-menus-in-EclipseGEF.aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=67fc1777-0fe6-4e70-85f5-ca5ba1db2270</guid>
      <pubDate>Mon, 22 Jun 2009 10:46:39 -0500</pubDate>
      <category>Software</category>
      <dc:publisher>jkealey</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=67fc1777-0fe6-4e70-85f5-ca5ba1db2270</pingback:target>
      <slash:comments>1</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=67fc1777-0fe6-4e70-85f5-ca5ba1db2270</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2009/06/22/Multi-level-contextual-menus-in-EclipseGEF.aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=67fc1777-0fe6-4e70-85f5-ca5ba1db2270</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=67fc1777-0fe6-4e70-85f5-ca5ba1db2270</feedburner:origLink></item>
    <item>
      <title>Eclipse Contextual Help in WizardDialog</title>
      <description>&lt;p&gt;Although &lt;a href="http://www.lavablast.com"&gt;LavaBlast&lt;/a&gt; mainly produces .NET-based solutions, we’re currently working on usability enhancements to our &lt;a href="http://blog.lavablast.com/post/2007/09/Requirements-Engineering-at-LavaBlast.aspx"&gt;favourite requirements engineering tool&lt;/a&gt;, &lt;a href="http://jucmnav.softwareengineering.ca/twiki/bin/view/ProjetSEG/WebHome"&gt;jUCMNav&lt;/a&gt;. jUCMNav is Java-based open source project (an &lt;a href="http://www.eclipse.org"&gt;Eclipse&lt;/a&gt; plug-in) for graphical software requirements modelling built using the &lt;a href="http://www.eclipse.org/gef"&gt;Graphical Editing Framework (GEF)&lt;/a&gt; and the &lt;a href="http://www.eclipse.org/emf"&gt;Eclipse Modeling Framework (EMF)&lt;/a&gt;.&amp;#160; Personally, I love the development cycle (with automatic incremental compilation) in Java/Eclipse. Writing plug-ins for Eclipse is tons of fun and there is an active community contributing to the Eclipse project (and its subprojects). However, I often find myself wanting to do something simple and having to fiddle around with 12 source code excerpts to make it work as a whole for various reasons. &lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;You know how to do something for general Eclipse plug-ins but not where to hook it up to your GEF editor. &lt;/li&gt;    &lt;li&gt;You know how to do something in SWT/Draw2D but not where to hook it up to your GEF editor. &lt;/li&gt;    &lt;li&gt;You get burned by random conventions that aren’t clearly defined &lt;/li&gt;    &lt;li&gt;Some file you would think would be included is ignored by your build script. &lt;/li&gt; &lt;/ul&gt;  &lt;ul&gt;In any case, I’ll post a few technical details on some of the issues we had to solve, hoping it will help someone out in the future! &lt;/ul&gt;  &lt;h2&gt;Scenario: You are using a WizardDialog in your application, but the ? button does nothing. &lt;/h2&gt;  &lt;p&gt;Or: How do you set up contextual help on a WizardDialog in Eclipse?&lt;/p&gt;  &lt;h3&gt;Step 1) Create a help_contexts.xml file. &lt;/h3&gt;  &lt;div class="csharpcode"&gt;   &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="html"&gt;xml&lt;/span&gt; &lt;span class="attr"&gt;version&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;1.0&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;encoding&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;UTF-8&amp;quot;&lt;/span&gt;?&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="html"&gt;NLS&lt;/span&gt; &lt;span class="attr"&gt;TYPE&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;org.eclipse.help.contexts&amp;quot;&lt;/span&gt;?&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;contexts&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;context&lt;/span&gt;  &lt;span class="attr"&gt;id&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;help_general&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;description&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;test&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;description&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;topic&lt;/span&gt; &lt;span class="attr"&gt;label&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;test&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;href&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;http://domain.com/help.html&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;   &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;context&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;contexts&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The name of the XML file is not important. &lt;/li&gt;

  &lt;li&gt;Important: &lt;strong&gt;DO NOT &lt;/strong&gt;include your plug-in name in the context id (here: “help_general”) &lt;/li&gt;

  &lt;li&gt;Important: &lt;strong&gt;DO NOT &lt;/strong&gt;include any periods in the context id (here: “help_general”, not “help.general”) &lt;/li&gt;

  &lt;li&gt;You may reference local help files – they don’t need to be external. 
    &lt;h3&gt;&amp;#160;&lt;/h3&gt;

    &lt;h3&gt;Step 2) Reference the contexts file from your plugin.xml&lt;/h3&gt;

    &lt;div class="csharpcode"&gt;
      &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;extension&lt;/span&gt; &lt;span class="attr"&gt;point&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;org.eclipse.help.contexts&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

      &lt;pre&gt;         &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;contexts&lt;/span&gt; &lt;span class="attr"&gt;file&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;help_contexts.xml&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

      &lt;pre class="alt"&gt;         &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;contexts&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

      &lt;pre&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;extension&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
    &lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;The contexts element has an optional plugin-id parameter. Leave this empty unless you want to contribute help contexts to another plug-in. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Step 3) Ensure help_contexts.xml is packaged with your application&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Edit your build.properties file to ensure it includes help_contexts.xml (bin.includes = …, help_contexts.xml, …) &lt;/li&gt;

  &lt;li&gt;Note the Bundle-SymbolicName in your Manifest.MF (also visible in your plugin.xml editor). If none found, note Bundle-Name.&amp;#160; Example: com.domain.myplugin &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Step 4) Set the context id in the WizardPage&lt;/h3&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; MyWizardPage extends WizardPage&lt;/pre&gt;

  &lt;pre&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; createControl(Composite parent) {&lt;/pre&gt;

  &lt;pre class="alt"&gt;        PlatformUI.getWorkbench.getHelpSystem.setHelp(parent, &lt;span class="str"&gt;&amp;quot;com.domain.myplugin.help_general&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre&gt;    }&lt;/pre&gt;

  &lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;You must add this in each WizardPage, not the WizardDialog. &lt;/li&gt;
&lt;/ul&gt;

&lt;ul&gt;Hope this helped!&lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/bW4BcBzQUOs" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/bW4BcBzQUOs/post.aspx</link>
      <author>jkealey</author>
      <comments>http://blog.lavablast.com/post/2009/06/22/Eclipse-Contextual-Help-in-WizardDialog.aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=143980ca-f494-4261-869d-756eff31ebd9</guid>
      <pubDate>Mon, 22 Jun 2009 10:08:14 -0500</pubDate>
      <category>Software</category>
      <dc:publisher>jkealey</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=143980ca-f494-4261-869d-756eff31ebd9</pingback:target>
      <slash:comments>1</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=143980ca-f494-4261-869d-756eff31ebd9</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2009/06/22/Eclipse-Contextual-Help-in-WizardDialog.aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=143980ca-f494-4261-869d-756eff31ebd9</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=143980ca-f494-4261-869d-756eff31ebd9</feedburner:origLink></item>
    <item>
      <title>YUI Compressor for Visual Studio</title>
      <description>&lt;p&gt;
Although you don&amp;#39;t want this for all things in life, you do want to ensure that your JavaScript and CSS files are as small as possible.&amp;nbsp; As a web programmer, a script minifier is a useful application that should be a part of your toolbelt. This article presents a simple way to hook up a popular minifer inside Visual Studio. 
&lt;/p&gt;
&lt;p&gt;
First you need to download &lt;a href="http://www.julienlecomte.net/yuicompressor/"&gt;YUI Compressor&lt;/a&gt; from Yahoo and unzip its contents anywhere. 
&lt;/p&gt;
&lt;h2&gt;External Tool&lt;/h2&gt; 
&lt;p&gt;
Now you want to make a new external tool in Visual Studio. (Tools -&amp;gt; External Tools...)
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/YUICompressorforVisualStudio/623D31BE/image.png"&gt;&lt;img style="border-width: 0px; display: inline" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/YUICompressorforVisualStudio/6FA344C4/image_thumb.png" border="0" alt="image" title="image" width="203" height="320" /&gt;&lt;/a&gt; 
&lt;/p&gt;
&lt;p&gt;
Then add a new tool and name it YUI Compressor.&amp;nbsp; You want to put the following values in the inputs.&amp;nbsp; Adjust the jar location depending of where you unzipped &lt;a href="http://www.julienlecomte.net/yuicompressor/"&gt;YUI Compressor&lt;/a&gt;.&amp;nbsp; It also assumes that &lt;a href="http://java.sun.com/"&gt;java.exe is in your path&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
&lt;a style="float: left" href="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/YUICompressorforVisualStudio/4EAFD21D/image.png"&gt;&lt;img style="border-width: 0px; margin: 0px 10px 10px 0px; display: inline" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/YUICompressorforVisualStudio/1217D380/image_thumb.png" border="0" alt="image" title="image" width="381" height="370" /&gt;&lt;/a&gt; 
&lt;/p&gt;
&lt;ol style="float: left"&gt;
	&lt;li&gt;&lt;strong&gt;Title&lt;/strong&gt;: Yui Compressor  &lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Command&lt;/strong&gt;: java.exe  &lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Arguments&lt;/strong&gt;: -jar &amp;quot;E:\yuicompressor-2.4.2\build\yuicompressor-2.4.2.jar&amp;quot; $(ItemPath) --charset &amp;quot;UTF8&amp;quot; --type js -o $(ItemFileName).min$(ItemExt)  &lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Initial Directory&lt;/strong&gt;: $(ItemDir)  &lt;/li&gt;
	&lt;li&gt;Check &amp;ldquo;Use Output Window&amp;rdquo; &lt;/li&gt;
&lt;/ol&gt;
&lt;p style="clear: both"&gt;
You can already test this.&amp;nbsp; Any problems will be presented in the Output window.&amp;nbsp; First you need to select your *.js file in the Solution Explorer.&amp;nbsp; Then in the Tool menu, select Yui Compressor.&amp;nbsp; Refresh the directory that contains your *.js file and you should have a file named *.min.js with the same prefix as your *.js.
&lt;/p&gt;
&lt;p style="clear: both"&gt;
You could do the same thing to minimize CSS just by making a new external tool with the &amp;ndash;type argument value changed to css instead of js.
&lt;/p&gt;
&lt;h2&gt;Toolbar&lt;/h2&gt; 
&lt;p&gt;
You can easily make a toolbar to quickly do this when you need it.&amp;nbsp; Go to Tools-&amp;gt;Customize.&amp;nbsp; Then New&amp;hellip;
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/YUICompressorforVisualStudio/30EE475E/image.png"&gt;&lt;img style="border-width: 0px; display: inline" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/YUICompressorforVisualStudio/1DCD1AB2/image_thumb.png" border="0" alt="image" title="image" width="371" height="287" /&gt;&lt;/a&gt; 
&lt;/p&gt;
&lt;p&gt;
Then go to the Command Tab and select Tools.
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/YUICompressorforVisualStudio/5CBE9B4D/image.png"&gt;&lt;img style="border-width: 0px; display: inline" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/YUICompressorforVisualStudio/74E205A8/image_thumb.png" border="0" alt="image" title="image" width="372" height="288" /&gt;&lt;/a&gt; 
&lt;/p&gt;
&lt;p&gt;
Drag and Drop the External Command 1 (or the # corresponding to your external tool if you have more than one) to the created toolbar.&amp;nbsp; Hit close and now you can put your new toolbar wherever you want.
&lt;/p&gt;
&lt;p&gt;
I hope you find this useful! Setting up tools this way inside Visual Studio is a good way to speed up the development process because you don&amp;#39;t need to memorize numerous command line parameters: everything is done with one click! 
&lt;/p&gt;
&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2009%2f05%2fYUI-Compressor-for-Visual-Studio.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2009%2f05%2fYUI-Compressor-for-Visual-Studio.aspx&amp;amp;border=660000&amp;amp;fgcolor=660000&amp;amp;bgcolor=FF9900&amp;amp;cbgcolor=FFFF00" border="0" alt="kick it on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/ZbUYPnjRsPI" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/ZbUYPnjRsPI/post.aspx</link>
      <author>EtienneT</author>
      <comments>http://blog.lavablast.com/post/2009/05/11/YUI-Compressor-for-Visual-Studio.aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=fe212bd8-31c2-48eb-80f9-a7b8974de983</guid>
      <pubDate>Mon, 11 May 2009 14:44:00 -0500</pubDate>
      <category>Software</category>
      <dc:publisher>EtienneT</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=fe212bd8-31c2-48eb-80f9-a7b8974de983</pingback:target>
      <slash:comments>14</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=fe212bd8-31c2-48eb-80f9-a7b8974de983</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2009/05/11/YUI-Compressor-for-Visual-Studio.aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=fe212bd8-31c2-48eb-80f9-a7b8974de983</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=fe212bd8-31c2-48eb-80f9-a7b8974de983</feedburner:origLink></item>
    <item>
      <title>Software Startup Lessons (Part 7) - Versatility</title>
      <description>&lt;p&gt;&lt;a href="http://www.flickr.com/photos/jpdaigle/3393870438/"&gt;&lt;img style="border-width: 0px; margin: 10px" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/SoftwareStartupLessonsPart7WhatIlearnedi_13D4B/rideau_3.jpg" border="0" alt="The many steps of launching a business..." width="222" height="331" align="right" /&gt;&lt;/a&gt; This is Part 7 of an ongoing series of lessons learned during the first years of our software startup. Feel free to take a look at our first year (&lt;a href="http://blog.lavablast.com/post/2008/03/Software-Startup-Lessons-(Part-1)-The-Basics.aspx"&gt;Part 1&lt;/a&gt;, &lt;a href="http://blog.lavablast.com/post/2008/03/Software-Startup-Lessons-(Part-2)-Communication-and-Collaboration.aspx"&gt;Part 2&lt;/a&gt;, &lt;a href="http://blog.lavablast.com/post/2008/03/Software-Startup-Lessons-(Part-3)---Marketing2c-Sales-amp3b-Growth.aspx"&gt;Part 3&lt;/a&gt;) and our second year (&lt;a href="http://blog.lavablast.com/post/2009/03/Software-Startup-Lessons-(Part-4)-ndash3b-Year-Two.aspx"&gt;Part 4&lt;/a&gt;, &lt;a href="http://blog.lavablast.com/post/2009/04/Software-Startup-Lessons-(Part-5)-ndash3b-Being-a-software-startup-in-a-recession.aspx"&gt;Part 5&lt;/a&gt;, &lt;a href="http://blog.lavablast.com/post/2009/04/Software-Startup-Lessons-(Part-6)-ndash3bLooking-back-at-one-failure.aspx#id_9f0c21ae-96df-435d-8315-50a0bbfa4ff6"&gt;Part 6&lt;/a&gt;). Today we'll talk about of the key lessons that we learned in university that helped us during the first two years of our business: versatility.&lt;/p&gt;
&lt;p&gt;We recently presented to a class of computer science students at the &lt;a href="http://www.site.uottawa.ca"&gt;University of Ottawa&lt;/a&gt; where we spoke of the single most important thing I learned in university: the importance of versatility. To make a long story short, we strongly feel that the more versatile you are, the more valuable you are to a software startup. Small startups don't always have the luxury of assigning roles to each employee (quality assurance manager, database administrator, usability expert, website maintenance, etc.) because of the team size. The founding team is responsible for all of the aspects of the business and they must face fresh challenges every day. The wider the breadth of experience, the better the team can propose cost-effective solutions to their customers. Today's software engineer must be aware of the tools at their disposal, whether they be open source or not. In a sense, this is where a founding team with complementary skills and experience is almost mandatory.&lt;/p&gt;
&lt;p&gt;University courses usually introduce subjects that self-learners wouldn't necessarily discover on their own. Although one might not directly apply the theory learned in courses, individuals grow their knowledge base which might eventually help them solve problems efficiently. As an example, data structure and algorithm course gives students the knowledge required to know when to use a hash table or merge sort, even if in practice (almost) no one implements min-max heaps on a daily basis. You probably won't be opening your probability charts for a Gaussian distribution this month, but the lessons learned do help you solve problems such as &amp;ldquo;which point of sale does an abnormal number of refunds&amp;rdquo; efficiently. University is all about gathering tools to be able to face all the problems you will encounter in your career.&lt;/p&gt;
&lt;p&gt;Furthermore, most new grads grasp the importance of software in a software business. However, lots of people forget about the &lt;a href="http://www.amazon.com/gp/product/1590596234?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1590596234"&gt;business aspects&lt;/a&gt;. When launching your own startup, you need to have some basic business experience: accounting, marketing, sales, legal, etc. If you've never done your own income tax or budget before and don't know much about software intellectual property, you'll eventually run into problems. In a small software startup, every dollar counts and you want those high-paid experts to do what they're suppose to: solve you hard problems. You don't want to be paying them 200+ dollars an hour to do data entry, but that's what will happen if you don't have a basic understanding of accounting.&lt;/p&gt;
&lt;p&gt;In addition, we feel that all computer scientists should have basic knowledge about the various &lt;a href="http://www.opensource.org/licenses"&gt;open source software licenses&lt;/a&gt; out there, even if they aren't working on open source projects. Why? Simply put, your goal as a software engineer is to avoid reinventing the wheel and write the least amount of new code (less code means less time writing it, less time testing it, less time supporting it). When solutions already exist and can be found on blogs or other sources, it is very tempting to re-use it. However, depending on the &lt;a href="http://en.wikipedia.org/wiki/Software_license"&gt;software license&lt;/a&gt;, bringing this code into your project can have significantly different consequences. Some licenses mean &amp;ldquo;&lt;a href="http://en.wikipedia.org/wiki/Permissive_free_software_licence"&gt;do what you want with it&lt;/a&gt;, but don't sue me if it doesn't work&amp;rdquo; while others mean &amp;ldquo;if you import these fifty lines of code into your project, &lt;a href="http://en.wikipedia.org/wiki/Copyleft"&gt;your project becomes open source&lt;/a&gt; and you must give a copy to anyone that requests it&amp;rdquo;. There are tons of nuances, but it doesn't take long to get up to speed. Although we learned this in &lt;a href="http://www.scs.carleton.ca/~deugo/comp5900/"&gt;Dwight Deugo's excellent course&lt;/a&gt;, anyone can get up to speed with a few hours of googling.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.rescuetime.com"&gt;&lt;img style="border-width: 0px; margin: 10px" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/SoftwareStartupLessonsPart7WhatIlearnedi_13D4B/rescuetime_3.png" border="0" alt="We use RescueTime" width="331" height="242" align="right" /&gt;&lt;/a&gt; One area we haven't mentioned yet is communication / social interactions. We track our time using &lt;a href="http://www.rescuetime.com/"&gt;RescueTime&lt;/a&gt; and, over the past 12 months, we can confirm than more than a third of our day is spent on communications. Not only do we collaborate within the team (project planning, testing, managing, etc.) but we also need to maintain relationships with outside parties (clients, prospects, suppliers, etc.). I think the simple fact that we're spending a third of our time talking/writing is justification enough to force ourselves to improve our communication skills. We're definitely not English majors, but we know how to express ideas in simple way that facilitates communication.&lt;/p&gt;
&lt;p&gt;In summary, if you want to launch your own software startup after university:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Take some business courses &lt;/li&gt;
&lt;li&gt;Take some law courses &lt;/li&gt;
&lt;li&gt;Try to improve your writing skills (reports, blog, etc.) &lt;/li&gt;
&lt;li&gt;Improve your communication skills (oral presentations) &lt;/li&gt;
&lt;li&gt;Try to avoid student loans! &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Get as much work experience as possible&lt;/strong&gt; (coop terms, summer internships) &lt;/li&gt;
&lt;li&gt;Do more than what is expected of you&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Passion is the key to success&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We've explained how versatility is important, but we should mention that passion is required. We feel it is critical than we tackle any work with the same passion that drives us when developing software. That means balancing bank account statements at 10PM on a Sunday night with passion, if needed. That means attentively reading a 20 page legal document, on a Friday afternoon when required. That means writing a quote with enthusiasm even if the last three failed and someone stole your car stereo last night.&lt;/p&gt;
&lt;p&gt;We never feel thrilled to do accounting, but once started, it is important we make the best of it and focus on the task at hand with as much passion as possible. Unfortunately, motivation needs to be intrinsic and cannot be imposed or learned. Some of us can find passion in certain tasks more easily than others, but it is possible to get the same thrill of getting things done regardless of how boring the task is in appearance. Programmers know the importance of getting &amp;ldquo;in the zone&amp;rdquo; where productivity is at its maximum. This &amp;ldquo;zone&amp;rdquo; is not exclusive to programmers or writers: it can be reached during any task. We're not psychologists in any way, but it does appear that constant observation of how a task is supposedly boring doesn't help productivity. Self-awareness and re-evaluating how a task is executed is definitely a good way to find process improvements, but it shouldn't get in the way of &lt;a href="http://www.amazon.com/gp/product/1590598385?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1590598385"&gt;getting things done&lt;/a&gt;. Convincing yourself that a task needs to be done, done well, and done efficiently is the first step to getting &amp;ldquo;in the zone&amp;rdquo;. Stop thinking about the pain and, eventually, it will go away. Passion improves throughput. Passion improves quality. Passion helps you get back to software development sooner (or whatever you like doing).&lt;/p&gt;
&lt;p&gt;While we're on the subject, we've heard another rule of thumb: success is one third hard work, one third contacts, and one third luck. Assuming you've got passion, you've got the hard work area covered. Assuming you've got passion, you'll be able to inspire other people and build a network of contacts. It's going to be harder, but you can do it. Finally, assuming you've got passion, you'll make your own luck. This is probably the hardest fact to accept in business: regardless of you/your team/your idea/your contacts, business is not an exact science. You can improve your chances, but nothing is guaranteed.&lt;/p&gt;
&lt;p&gt;This concludes the lessons learned in our second year. I would not be surprised if we added more lessons in twelve months, as we never stop learning new things! If you're thinking of taking the plunge, you should as it is definitely worth it!&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2009%2f04%2fSoftware-Startup-Lessons-(Part-7)-ndash3b-Versatility.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2009%2f04%2fSoftware-Startup-Lessons-(Part-7)-ndash3b-Versatility.aspx&amp;amp;border=660000&amp;amp;fgcolor=660000&amp;amp;bgcolor=FF9900&amp;amp;cbgcolor=FFFF00" border="0" alt="kick it on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/LI3Lz57pOy0" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/LI3Lz57pOy0/post.aspx</link>
      <author>JKealey</author>
      <comments>http://blog.lavablast.com/post/2009/04/20/Software-Startup-Lessons-(Part-7)-ndash3b-Versatility.aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=b7f7139a-4077-4e45-a1f5-e24919a0bf19</guid>
      <pubDate>Mon, 20 Apr 2009 11:41:00 -0500</pubDate>
      <category>Business</category>
      <dc:publisher>JKealey</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=b7f7139a-4077-4e45-a1f5-e24919a0bf19</pingback:target>
      <slash:comments>4</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=b7f7139a-4077-4e45-a1f5-e24919a0bf19</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2009/04/20/Software-Startup-Lessons-(Part-7)-ndash3b-Versatility.aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=b7f7139a-4077-4e45-a1f5-e24919a0bf19</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=b7f7139a-4077-4e45-a1f5-e24919a0bf19</feedburner:origLink></item>
    <item>
      <title>Software Startup Lessons (Part 6) &amp;ndash;Looking back at one failure</title>
      <description>&lt;p&gt;
&lt;a href="http://www.lavablast.com" title="Jason's glasses have decided to follow their own path."&gt;&lt;img style="border-width: 0px; margin: 10px" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/SoftwareStartupLessonsPart5Lookingbackat_13CBB/glasses_3.jpg" border="0" alt="glasses" width="306" height="230" align="right" /&gt;&lt;/a&gt; This is Part 6 of an ongoing series of lessons learned during the first years of our software startup. Feel free to take a look at our first year (&lt;a href="http://blog.lavablast.com/post/2008/03/Software-Startup-Lessons-(Part-1)-The-Basics.aspx"&gt;Part 1&lt;/a&gt;, &lt;a href="http://blog.lavablast.com/post/2008/03/Software-Startup-Lessons-(Part-2)-Communication-and-Collaboration.aspx"&gt;Part 2&lt;/a&gt;, &lt;a href="http://blog.lavablast.com/post/2008/03/Software-Startup-Lessons-(Part-3)---Marketing2c-Sales-amp3b-Growth.aspx"&gt;Part 3&lt;/a&gt;) and our second year (&lt;a href="http://blog.lavablast.com/post/2009/03/Software-Startup-Lessons-(Part-4)-ndash3b-Year-Two.aspx"&gt;Part 4&lt;/a&gt;, &lt;a href="http://blog.lavablast.com/post/2009/04/Software-Startup-Lessons-(Part-5)-ndash3b-Being-a-software-startup-in-a-recession.aspx"&gt;Part 5&lt;/a&gt;). Today we&amp;#39;ll talk about one of our failures.  
&lt;/p&gt;
&lt;p&gt;
When you run your own company, you never run out of things to learn. We feel we&amp;#39;ve made great progress learning from our successes but mainly our mistakes. As Bill Gates once said, &amp;ldquo;It&amp;#39;s fine to celebrate success but it is more important to heed the lessons of failure&amp;rdquo;.  
&lt;/p&gt;
&lt;p&gt;
In this line of thought, it is much easier (and faster!) to learn from the failures of other people than your own. It is for this reason that &lt;a href="http://www.amazon.com/gp/product/1590597214?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1590597214"&gt;In Search of Stupidity&lt;/a&gt; and &lt;a href="http://www.amazon.com/gp/product/1430210788?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1430210788"&gt;Founders At Work&lt;/a&gt; are on our recommended reading list for anyone launching a software business. We&amp;#39;ve talked about the books before on this blog, but must mention them again, as they are such a great reads.  
&lt;/p&gt;
&lt;p&gt;
Learning from failures is an important part of self-enlightenment but failure, as you can expect, is not something people like to share with others. Therefore, it is hard to find good stories that describe the steps that lead to failure and what could have been done to turn the failure into a success story. In this spirit, I feel it is important to describe one of LavaBlast&amp;#39;s failures. I hope that this will encourage those of you who also run startups to post about your own failures, so that we can collectively learn from our experiences.  
&lt;/p&gt;
&lt;p&gt;
Even though we build software for the franchise industry, we cultivate a love-hate relationship with it. We decided to launch a business that focuses on franchises for many reasons; one of them being we fill a need in the market. Simply put, we build operational software: our clients need our software to run their business. Amongst the other software companies that build software for the franchise industry, many of them focus on converting web visitors into franchisees, in exchange for a hefty commission. This populates (read pollutes) the Internet with thousands of sites focusing on franchise opportunities. This is something we strongly disliked as it makes it hard to find anything related to franchising on the Internet without landing on one of these websites. (Don&amp;#39;t get me wrong... these sites do provide a good service, but make it hard to find anything else.)  
&lt;/p&gt;
&lt;p&gt;
Being users of &lt;a href="http://www.dotnetkicks.com/"&gt;DotNetKicks&lt;/a&gt;, a site that aggregates news/articles/blog posts about &lt;a href="http://www.microsoft.com"&gt;Microsoft&lt;/a&gt; technologies, we thought it would be a good idea to launch a similar site based on the franchise industry. The end result would be a community-driven franchise news site that keeps people informed of what was going on in the franchise world: franchisors going bankrupt, &lt;a href="http://www.unhappyfranchisee.com/"&gt;unhappy franchisees&lt;/a&gt;, new franchises, franchise trends, franchise humor, etc.  
&lt;/p&gt;
&lt;p&gt;
Normally we would have said that this was a crazy project as there was nothing in it for us in exchange for hundreds of hours of programming time. However, &lt;a href="http://www.dotnetkicks.com"&gt;DotNetKicks&lt;/a&gt; being an open source engine built using the same technologies that we use on a daily basis, we figured it would be easy enough to launch our own engine based on this code. Thus, &lt;a href="http://news.lavablast.com"&gt;Franchise NewsBlast&lt;/a&gt; was born. In less than a day&amp;#39;s work, we had the site up and running and ready to receive content.  
&lt;/p&gt;
&lt;p&gt;
We knew we had to create some base content to generate interest and get the ball rolling. We therefore carefully read hundreds of articles and picked the cream of the crop to post on &lt;a href="http://news.lavablast.com"&gt;Franchise NewsBlast&lt;/a&gt;. We wanted to fill every section EXCEPT for franchise opportunities. Once that was complete, we contacted hundreds of franchise-related websites to inform them about our new engine. We promoted our site to the few &lt;a href="http://thefranchiseking.typepad.com/"&gt;bloggers&lt;/a&gt; in this space. We wrote a press release and sent it on a few channels. We wrote blog posts on &lt;a href="http://www.bluemaumau.org"&gt;Blue Mau Mau&lt;/a&gt;, the largest community-driven franchise website (which we also published here).  
&lt;/p&gt;
&lt;p&gt;
To make a long story short, after investing over a hundred hours (most of which in promotion, as the coding had already been done), we had one subscriber. Yes... only one person registered to post and rate articles on Franchise NewsBlast. ONE person joined our free site. This person also runs &lt;a href="http://thefranchiseking.typepad.com/"&gt;twelve-or-so franchise related blogs&lt;/a&gt;. Obviously, he registered to self-promote and we were happy about this as this is exactly what the site was intended to do... but when there&amp;#39;s no community to rate the posts, the site has no value. We never reached &lt;a href="http://www.amazon.com/gp/product/0316346624?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0316346624"&gt;the tipping point&lt;/a&gt; for it to go viral.  
&lt;/p&gt;
&lt;p&gt;
Practice makes perfect. Over the course of the following months, we signed up to various &lt;a href="http://franchisebrief.com"&gt;franchise news sources&lt;/a&gt; and cross-posted relevant articles. As time passed, we did gain readers but very few posters. We also gained spammers that were obliged to block. We reduced our quality standards in order to keep cross-posting on a regular basis. After three months, the site still stagnated and we discussed the inevitable: shutting the service down.  
&lt;/p&gt;
&lt;p&gt;
Three months later, the site was still online as it costs next to nothing to host. However, we&amp;#39;re shutting it down today as it hasn&amp;#39;t attracted any interest since. We&amp;#39;re officially calling this project a failure. Why did it fail? Was it the software? No, the software is great &amp;ndash; take a look at the &lt;a href="http://www.dotnetkicks.com"&gt;DotNetKicks&lt;/a&gt; website. Was it lack of marketing? I don&amp;#39;t think so. We did invest tons of time initially to make this work as we wanted this to be our gift to the franchise community.  
&lt;/p&gt;
&lt;p&gt;
Before starting this project, we did not know if the community would be interested in this online service. We thought it was a risky project, but were willing to lose a few hours to promoting our altruistic gesture. In the end, we believe there are simply not enough people that are interested in this type of service. If this is not the case, then these people are simply not computer savvy enough to see the value in such a service and/or find us. The last possibility is that we didn&amp;#39;t promote it to enough people, even if we gave it our all. (Of course, we never paid a dime for advertising which might have helped us reach the tipping point.)  
&lt;/p&gt;
&lt;p&gt;
&lt;a href="http://www.flickr.com/photos/jpdaigle/3255057336/" title="Three strikes and you're out."&gt;&lt;img style="border-width: 0px; margin: 10px" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/SoftwareStartupLessonsPart5Lookingbackat_13CBB/threestrikes_3.jpg" border="0" alt="threestrikes" width="345" height="226" align="right" /&gt;&lt;/a&gt; We&amp;#39;ve decided that the root cause of this failure was misreading the community and distorting our perspective on the market. We looked at the franchise market from a software engineering perspective: a classic mistake made by developers. This is exactly the reason why most software is unusable: developers don&amp;#39;t spend enough time thinking like people or getting feedback from users.  
&lt;/p&gt;
&lt;p&gt;
Since we launched &lt;a href="http://news.lavablast.com"&gt;Franchise NewsBlast&lt;/a&gt;, an online franchise communities called &lt;a href="http://www.franmarket.com/"&gt;FranMarket&lt;/a&gt; was launched using the Ning social network generator. &lt;a href="http://www.franmarket.com/"&gt;Franchise Market Magazine&lt;/a&gt; is the originator of this community and we are happy to see they&amp;#39;ve started building the online franchise community. They&amp;#39;ve got more users, but our blog has more traffic than they do, according to &lt;a href="http://www.alexa.com"&gt;Alexa&lt;/a&gt;. &lt;a href="http://www.franchisebrief.com/"&gt;Franchise Brief&lt;/a&gt; is probably the simplest yet most active franchise new aggregator we&amp;#39;ve found but it doesn&amp;#39;t appear to have lots of visitors.&amp;nbsp; &lt;a href="http://www.bluemaumau.org"&gt;Blue Mau Mau&lt;/a&gt; is still the biggest player in the online franchise community, and it is &lt;strong&gt;the&lt;/strong&gt; online community for Franchise &lt;a href="http://www.amazon.com/gp/product/0316346624?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0316346624"&gt;mavens&lt;/a&gt;.  
&lt;/p&gt;
&lt;p&gt;
Why are online communities failing in the franchise world? There are many reasons, but we can&amp;#39;t claim to know them all. The franchise world is composed of franchisors, franchisees, franchise prospects, and franchise service providers.  
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Franchisors&lt;/strong&gt;: There aren&amp;#39;t that many around. They&amp;#39;re not the bulk of the community.  &lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Franchisees&lt;/strong&gt;: There are more franchisees and we can see them being very vocal about the issues they have with their franchisor on sites like &lt;a href="http://www.bluemaumau.org"&gt;Blue Mau Mau&lt;/a&gt;. However, most of them are probably too busy running their business to be spending time learning about events in other franchise systems. (And they have better sources than public websites for news about their own franchise.)  &lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Franchise prospects&lt;/strong&gt;: Prospects are the largest part of the community. They are interested in hearing everyone gossip about a franchise they&amp;#39;re thinking of buying when doing their due diligence. However, once they do buy, they&amp;#39;re probably don&amp;#39;t care about what&amp;#39;s going on in the franchise world anymore (as they are now franchisees).  &lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Franchise service providers&lt;/strong&gt;: There are lots of such consultants/firms, but as you can imagine the goal is to sell services to others. It&amp;#39;s the equivalent to putting a hundred lawyers in the same room as six startup founders. The service providers are not generating the news and hence are not usually that interesting (there are some exceptions &amp;ndash; &lt;a href="http://bizop.ca/blog2/"&gt;Michael Webster&lt;/a&gt;). Service providers like &lt;a href="http://www.lavablast.com"&gt;LavaBlast&lt;/a&gt; are part of a healthy community, but we&amp;#39;re not what defines it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
What&amp;#39;s left? Not that many people: and the cream of the crop is already using other services such as &lt;a href="http://www.bluemaumau.org"&gt;Blue Mau Mau&lt;/a&gt;. We ignored the classic &amp;ldquo;know your market&amp;rdquo; recommendation. We feel that&amp;#39;s why we failed. We are disappointed but we don&amp;#39;t regret trying out &lt;a href="http://news.lavablast.com"&gt;Franchise NewsBlast&lt;/a&gt;. After all, we did learn more about the franchise world and we did make a few contacts. Best of all, it gave us a story to write! 
&lt;/p&gt;
&lt;p&gt;
Now that we&amp;#39;ve told you about our failure, we would truly appreciate it if you did the same! Think about your recent failures and blog about them! Everyone fails once in a while! There&amp;#39;s no shame in failure as if you never try anything, you&amp;#39;ll never go anywhere! 
&lt;/p&gt;
&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2009%2f04%2fSoftware-Startup-Lessons-(Part-6)-ndash3bLooking-back-at-one-failure.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2009%2f04%2fSoftware-Startup-Lessons-(Part-6)-ndash3bLooking-back-at-one-failure.aspx&amp;amp;border=660000&amp;amp;fgcolor=660000&amp;amp;bgcolor=FF9900&amp;amp;cbgcolor=FFFF00" border="0" alt="kick it on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/WpfwG88PziI" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/WpfwG88PziI/post.aspx</link>
      <author>JKealey</author>
      <comments>http://blog.lavablast.com/post/2009/04/14/Software-Startup-Lessons-(Part-6)-ndash3bLooking-back-at-one-failure.aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=353bb19f-c31c-4cf2-aa36-eef9ca61c5b1</guid>
      <pubDate>Tue, 14 Apr 2009 10:57:00 -0500</pubDate>
      <category>Business</category>
      <category>Franchising</category>
      <category>Ideas</category>
      <dc:publisher>JKealey</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=353bb19f-c31c-4cf2-aa36-eef9ca61c5b1</pingback:target>
      <slash:comments>10</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=353bb19f-c31c-4cf2-aa36-eef9ca61c5b1</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2009/04/14/Software-Startup-Lessons-(Part-6)-ndash3bLooking-back-at-one-failure.aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=353bb19f-c31c-4cf2-aa36-eef9ca61c5b1</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=353bb19f-c31c-4cf2-aa36-eef9ca61c5b1</feedburner:origLink></item>
    <item>
      <title>Software Startup Lessons (Part 5) - Being a software startup in a recession</title>
      <description>&lt;p&gt;&lt;a href="http://leighs-gallery.smugmug.com/gallery/6211293_bqkdo"&gt;&lt;img style="border-width: 0px; margin: 10px" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/SoftwareStartupLessonsPart6Beingasoftwar_13D1E/LavaBlast_3.jpg" border="0" alt="Leigh Hilbert Photography captured a Lava Blast!" width="333" height="223" align="right" /&gt;&lt;/a&gt; We're six months late to inform people that you can/should still start a software company in a downturn as this has been covered already &lt;a href="http://www.paulgraham.com/badeconomy.html"&gt;here&lt;/a&gt;, &lt;a href="http://www.startupottawa.com/?p=401"&gt;here&lt;/a&gt;, &lt;a href="http://www.inc.com/magazine/20080501/starting-up-in-a-down-economy.html"&gt;here&lt;/a&gt;, &lt;a href="http://montrealtechwatch.com/2009/01/17/the-economic-crisis-sucks-what-you-can-do-about-it/"&gt;here&lt;/a&gt;, and &lt;a href="http://blog.truereckoning.com/?p=51"&gt;here&lt;/a&gt;.&amp;nbsp; Why are we six months late informing you of this, you ask? We have been incredibly busy building software for our existing / new customers during this period. For reasons left unexplained, we've seen the number of leads in our sales pipeline increase dramatically since the start of the recession. Furthermore, people have been coming to us for consulting services thanks to the reputation we've built since we launched &lt;a href="http://www.lavablast.com"&gt;LavaBlast&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If we had to name a single element that has helped us / will help us during the recession, it would definitely be our&lt;strong&gt; capacity to discover commonalities between seemingly different situations, abstracting them out and generalizing the problem&lt;/strong&gt;. We can build systems for people who may not be in the franchise industry, but have similar needs. &lt;a href="http://comefortheride.com/how-long-is-your-startups-runway/"&gt;This lengthens our runway&lt;/a&gt;. This is one of the skills that &lt;a href="http://www.site.uottawa.ca/eng/school/ugrad/softwareengineering.html"&gt;we learned in university&lt;/a&gt; (more on this subject in Part 7).&lt;/p&gt;
&lt;p&gt;If you take a deeper look at what &lt;a href="http://www.lavablast.com"&gt;LavaBlast&lt;/a&gt; does (building customized software that helps collaboratively manage a franchise) it is easy to notice that we're solving a problem in a particular vertical that is present in numerous other business contexts. We build operational line-of-business applications (aka help-me-perform-my-daily-tasks-easily software) that are used by franchise owners and franchisors (aka different-users-can-see-different-parts-of-the-data-while-sharing-some-of-it software). Without going into greater detail, many businesses are looking for software that helps them simplify their day-to-day operations and reduce costs, recession or not. It might not be as scalable as a consumer-focused website and not as glamorous as other projects, but it does have its challenges and is a great type of business that one can bootstrap!&lt;/p&gt;
&lt;p&gt;[ We'd like to thank &lt;a href="http://leighs-gallery.smugmug.com/gallery/6211293_bqkdo"&gt;Leigh Hilbert&lt;/a&gt; for the &lt;a href="http://leighs-gallery.smugmug.com/gallery/6211293_bqkdo"&gt;"Lava Blast" picture&lt;/a&gt; seen above. ]&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;strong&gt;A mix of software products and services&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;In the &lt;a href="http://blog.lavablast.com/post/2008/03/Software-Startup-Lessons-(Part-1)-The-Basics.aspx"&gt;Part 1&lt;/a&gt;, written last year, we describe how LavaBlast builds products (&lt;a href="http://www.lavablast.com/en/lb_franchiseblast.aspx"&gt;franchise management solution&lt;/a&gt;, &lt;a href="http://lavablast.com/en/lb_pointofsale.aspx"&gt;franchise point of sale&lt;/a&gt;, etc.) but also services (as we adapt our software to each franchise's business processes). To help sustain development in a bootstrapped startup, software consulting is often a necessary &amp;ldquo;evil&amp;rdquo;. This year, we did do some consulting but managed to make the best of it. We've made a few interesting realizations that we've shared at a recent &lt;a href="http://teamcamp.pbwiki.com/"&gt;TeamCamp&lt;/a&gt; event at &lt;a href="http://www.thecodefactory.ca"&gt;The Code Factory&lt;/a&gt; and would like to re-iterate here. This discussion assumes you're building software for other businesses instead of consumers (for obvious reasons, it is easier to bootstrap a software startup that targets businesses).&lt;/p&gt;
&lt;p&gt;Typically, software consulting companies produce the same kind of software a couple times for different clients before deciding that it would be a good idea to build a product that addresses this same problem. At this point, they've delivered source code to each of their customers, as the customers retained the intellectual property rights related to the produced software. Therefore, to build a product and commercialize it, the consultants need to start from scratch. Although this may seem bad as the firm loses time and money rebuilding the product, it typically allows them to &amp;ldquo;build it right&amp;rdquo; thanks to the lessons learned during the first iterations. The product's architecture is well implemented as the main variation points have been clearly defined.&lt;/p&gt;
&lt;p&gt;Simply put, we did the opposite and have kept the intellectual property rights from day one. (How? We got &lt;em&gt;lucky&lt;/em&gt; that our customers had limited funds to invest and knew the value of intellectual property.) We sprinted for over a year building the core of our solution that allows retail stores and e-commerce websites to communicate with a &lt;a href="http://www.lavablast.com/en/lb_features.aspx"&gt;centralized franchise management application&lt;/a&gt;. This was a large undertaking, but we had our first customer already using the product and paying for its development, while we kept the rights to the source code. (Note: we still did multiple iterations and learned from our mistakes!) Once completed, the core was easy to adapt to different franchise systems because we're experts at rapid application development and because our core architecture allows us to vary software behaviour for each franchise (thank you the &lt;a href="http://en.wikipedia.org/wiki/Dependency_injection"&gt;strategy design pattern&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Dependency_injection"&gt;dependency injection&lt;/a&gt;!).&lt;/p&gt;
&lt;p&gt;What's interesting to note here is that because we own the intellectual property for the core of our system, it is much easier to sell enhancements to our core (to new customers) while preserving the rights to the source code for the combined system. It is also easier to find new customers &lt;em&gt;because &lt;/em&gt;the core is already built. Simply put, investing a year into a software product is an investment that keeps on giving, even in the services arena. Because we have a flexible core that has already been implemented and we're keeping the IP, it helps us keep costs down during a bad economy. This is a win-win situation for both parties!&lt;/p&gt;
&lt;h2&gt;Advantages for the software startup&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Retain the intellectual property &lt;/li&gt;
&lt;li&gt;Build applications faster, giving you time to work on other things &lt;/li&gt;
&lt;li&gt;Keep costs down - easier to find clients in bad economic times&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Advantages for the client&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Lower cost &lt;/li&gt;
&lt;li&gt;Put the software to use quickly &lt;/li&gt;
&lt;li&gt;Lower project risk &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;To get back to the discussion we had at &lt;a href="http://teamcamp.pbwiki.com"&gt;TeamCamp&lt;/a&gt;, the question was how do you turn your service business into a product-based software startup when you have limited/no funds, have limited/no leads, and own limited/no intellectual property? Well, I'm sad to say it, but it &amp;ldquo;&lt;a href="http://www.urbandictionary.com/define.php?term=sucks+to+be+you"&gt;sucks to be you&lt;/a&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You have to break the perpetual cycle you're currently in and do something different. &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For some people, that means realizing that you're never going to make a decent living building static websites for $200 when you've got to spend 20 hours with the customer to figure out where they want the pictures of their &lt;a href="http://ihasahotdog.com/"&gt;puppies&lt;/a&gt; on their upcoming site before actually starting the work. Your competition is doing it at half the price with pre-built templates, stock photography and, as an added bonus if you order within the next 24h, offering them a box of branded pens, 500 full-colour business cards and a mention on their next &lt;a href="http://www.twitter.com/jkealey"&gt;Twitter&lt;/a&gt; post. You are a commodity.&lt;/p&gt;
&lt;p&gt;For others, the decision boils down to what short term loss can do accept for possible future gains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build a product, build your reputation, and try to sell enhancements to customers while retaining the IP.   	
&lt;ul&gt;
&lt;li&gt;Tradeoff: money. You don't earn much revenue while doing this (often nothing during the first months). If you don't have prospects and don't know if your idea is any good, this is risky. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Assuming you can't afford this, build something during weekends and evenings and reap the rewards when it is complete.   	
&lt;ul&gt;
&lt;li&gt;Tradeoff: time. It takes five times as long to build it. However, you aren't screwed if things don't work out because your regular work pays the bills. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Build a quick alpha version and see what happens. Spark interest? Find funders? Find partners? Abandon your crazy idea?   	
&lt;ul&gt;
&lt;li&gt;Tradeoff: features &amp;amp; quality. It is preferable to fail quickly if you are bound to fail. I'd prefer investing a week of my time and getting proper feedback from peers informing me that my product idea sucks and I am bound to fail than eating cheap noodles for six months before discovering than no one will buy my completed product. Talk to people at events like &lt;a href="http://teamcamp.pbwiki.com"&gt;TeamCamp&lt;/a&gt; or &lt;a href="http://barcamp.org/DemoCamp"&gt;DemoCamp&lt;/a&gt; - &lt;a href="http://www.secretgeek.net/brilliantidea,asp.asp"&gt;stop being scared someone will steal your idea&lt;/a&gt;. Don't ask Mom or your beer buddies as they won't be harsh enough on you (although some of them might be mean drunks!). &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One tip that we do want to give fellow bootstrappers out there is that, in the early days,&lt;strong&gt; you can give the customer a non-restrictive copy of the code, while retaining ownership for yourself&lt;/strong&gt;. That way, both parties have a copy of the source code and both parties can do whatever they want with it, including selling it to others. However, you're the developer and the customer has better things to do than commercialize your software: all they care about is being able to maintain the code when your contract with them is over. This is a win-win situation for both parties, especially when you've managed to collect various modules that you can re-use for different customers.&lt;/p&gt;
&lt;h2&gt;How to launch a software startup in a recession&lt;/h2&gt;
&lt;p&gt;(Precondition: Start something that you can sell to businesses to lower the risk. )&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Find a first potential customer. Sign contract that says the intellectual property is yours but they get a copy of their version and they can do anything with it.   	
&lt;ul&gt;
&lt;li&gt;At this point, your software is worth nothing more than what the first customer is willing to pay for it. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Take your core software and refine it with other customers.   	
&lt;ul&gt;
&lt;li&gt;This time, try to keep the source code to yourself, as it is starting to build value. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Repeat step 2 until you've got enough funds and a high quality product.   	
&lt;ul&gt;
&lt;li&gt;Your product is now valuable. You are now out of the perpetual cycle. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sell copies and grow your business   	
&lt;ul&gt;
&lt;li&gt;This is where LavaBlast is at now, after two years. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;[insert secret sauce here] &lt;/li&gt;
&lt;li&gt;Success!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Considering all that has been said about launching a software startup in a recession, I think it all boils down to asking yourself "&lt;strong&gt;is this the right time for me?&lt;/strong&gt;". Software startups / &lt;a href="http://www.amazon.com/gp/product/1590596013?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1590596013"&gt;Micro-ISVs&lt;/a&gt; are tiny in comparison to what's going on at the macroeconomic level. Before taking the risk to launch your own business, what matters is the presence of the following elements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dedication / Passion / Interest &lt;/li&gt;
&lt;li&gt;Capacity to execute on the idea &lt;/li&gt;
&lt;li&gt;Support (family, friends, partners) &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2009%2f04%2fSoftware-Startup-Lessons-(Part-5)-ndash3b-Being-a-software-startup-in-a-recession.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2009%2f04%2fSoftware-Startup-Lessons-(Part-5)-ndash3b-Being-a-software-startup-in-a-recession.aspx&amp;amp;border=660000&amp;amp;fgcolor=660000&amp;amp;bgcolor=FF9900&amp;amp;cbgcolor=FFFF00" border="0" alt="kick it on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/BG1M9wv9pXI" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/BG1M9wv9pXI/post.aspx</link>
      <author>JKealey</author>
      <comments>http://blog.lavablast.com/post/2009/04/06/Software-Startup-Lessons-(Part-5)-ndash3b-Being-a-software-startup-in-a-recession.aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=8a303515-fadc-43db-b124-5ba0fd52478a</guid>
      <pubDate>Mon, 06 Apr 2009 10:47:00 -0500</pubDate>
      <category>Business</category>
      <dc:publisher>JKealey</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=8a303515-fadc-43db-b124-5ba0fd52478a</pingback:target>
      <slash:comments>3</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=8a303515-fadc-43db-b124-5ba0fd52478a</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2009/04/06/Software-Startup-Lessons-(Part-5)-ndash3b-Being-a-software-startup-in-a-recession.aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=8a303515-fadc-43db-b124-5ba0fd52478a</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=8a303515-fadc-43db-b124-5ba0fd52478a</feedburner:origLink></item>
    <item>
      <title>Software Startup Lessons (Part 4) - Year Two</title>
      <description>&lt;p&gt;&lt;a href="http://www.amazon.com/gp/product/B000O178BY?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B000O178BY"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; margin: 10px; border-right-width: 0px" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/SoftwareStartupLessonsPart4YearTwo_13C97/yearzero_3.jpg" border="0" alt="Year Zero was launched a few days after LavaBlast's incorporation!" width="244" height="244" align="right" /&gt; LavaBlast&lt;/a&gt; is now two years old. Last year, at around the same time, we wrote a series of blog posts (&lt;a href="http://blog.lavablast.com/post/2008/03/Software-Startup-Lessons-(Part-1)-The-Basics.aspx"&gt;Part 1&lt;/a&gt;, &lt;a href="http://blog.lavablast.com/post/2008/03/Software-Startup-Lessons-(Part-2)-Communication-and-Collaboration.aspx"&gt;Part 2&lt;/a&gt;, &lt;a href="http://blog.lavablast.com/post/2008/03/Software-Startup-Lessons-(Part-3)---Marketing2c-Sales-amp3b-Growth.aspx"&gt;Part 3&lt;/a&gt;) describing the lessons learned during our first year in operation as a software startup. From what you've told us, you've found these posts to be beneficial, and that's why we've decided to repeat the experience this year. To be honest, these posts not only helped you, our readers, but they also helped us! They helped us get known not only in the &lt;a href="http://www.startupottawa.com/?p=881"&gt;Ottawa&lt;/a&gt; and &lt;a href="http://www.montrealtechwatch.com/"&gt;Montreal&lt;/a&gt; start-up communities, but also &lt;a href="http://itscommonsensestupid.blogspot.com"&gt;internationally&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Part 4 gives a high-level summary of our past year. &lt;a href="http://blog.lavablast.com/post/2009/04/Software-Startup-Lessons-(Part-5)-ndash3b-Being-a-software-startup-in-a-recession.aspx"&gt;Part 5 will describe the life of a software startup in a recession&lt;/a&gt;. Part 6 will look back on one of our failures. Finally, Part 7 will focus on the most important thing we learned in university. We look forward to hearing your comments.&lt;/p&gt;
&lt;h2&gt;Introversion and Extraversion&lt;/h2&gt;
&lt;p&gt;Thinking back at our first year, our focus was developing our core solution and we were introverts. 90% of our focus was engineering and the remaining 10% was mainly marketing by building our &lt;a href="http://www.lavablast.com"&gt;website&lt;/a&gt;. In a nutshell, we built what we had to build, and focused on the building the innards of &lt;a href="http://lavablast.com/en/lb_franchiseblast.aspx"&gt;LavaBlast's core software solution&lt;/a&gt;. Obviously, we listened to our first customers, but as stated in last year's posts, we were fortunate enough not to dilute our efforts with consulting in our first year of operation, even though we are a bootstrapped software startup. Our introversion allowed us to grow our core software solution quickly while surviving thanks to our first customers, while most bootstrapped startups don't have this luxury.&lt;/p&gt;
&lt;p&gt;Looking back at our second year, however, our focus was finding new customers and growing the business. Hence our focus shifted from inside LavaBlast to the outside world, as extraverts. We participated in numerous local events, lots of them via &lt;a href="http://www.thecodefactory.ca"&gt;The Code Factory&lt;/a&gt;, and met tons of people. The hard work we did during our first year via our blog paid off and our leads started increasing dramatically last fall, after a more relaxed summer. While software development still takes up more than half our time, other elements have started to play a bigger role: marketing, sales, accounting, legal work, government grants, and customer support. Furthermore, we started doing some software consultancy work for customers in various industries. More about that next week, in Part 5.&lt;/p&gt;
&lt;p&gt;This change of pace did require some adjustments, but all-in-all, we're learning exactly what we set out to learn: how to launch a software startup. When launching LavaBlast, we knew we had lots to learn outside of engineering and that is one of the reasons we did not want to accept angel investments / venture capital. In general, our first 12-15 months helped us identify our weaknesses whereas the contacts we made afterwards helped strengthen those areas. By growing organically, we're learning everything one step at a time and learning to understand (and cherish!) the challenges outside of engineering. Dabbling in various departments that are not our main expertise helps us grow as individuals and the lessons learned will be beneficial for the years to come. Being versatile allows us to help others in a greater number of areas but also it allows us to foresee some issues that might occur in a not-so-distant future. More about this in a few weeks, in Part 7.&lt;/p&gt;
&lt;p&gt;Doing more than just software also helped us confirm the theory that it &lt;a href="http://www.joelonsoftware.com/articles/VC.html"&gt;takes a decade to build a successful software company&lt;/a&gt;. In terms of software produced, the core doesn't take that long to build. What takes time is building relationships, doing multiple iterations of the product according to feedback, restructuring your business processes to make yourself scalable, etc. Our second birthday is a major milestone given the large percentage of businesses that &lt;a href="http://smallbiztrends.com/2005/07/business-failure-rates-highest-in.html"&gt;fail within their first two years&lt;/a&gt;, especially in our industry. However, given the long term perspective, we still have a long way to go.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Know what's out there.&lt;/h2&gt;
&lt;p&gt;A few weeks after &lt;a href="http://www.thecodefactory.ca"&gt;The Code Factory&lt;/a&gt; opened, we attended a few events that were meant to inform founders of various funding opportunities that are out there. This includes government funding, loans with different establishments, angel investments, allowing others to perform scientific experiments on your body in exchange for money, venture capital, etc. As an example, we learned about the &lt;a href="http://www.cra-arc.gc.ca/sred/"&gt;SR&amp;amp;ED&lt;/a&gt; and &lt;a href="http://irap-pari.nrc-cnrc.gc.ca/"&gt;IRAP&lt;/a&gt; government programs. Simply put, having spent some 18 months doing research and development while building LavaBlast's core software solution, these programs allow us to claim a substantial portion of our R&amp;amp;D wages in refundable tax credits. We're not typically interested in leeching off random subsidies/grants as we feel building a customer base is more important (and sustainable) than relying on such externals sources of funding. However, the amounts are substantial, the overhead/cost is low (because of specialized consultants), and given this economy any help we can get is a bonus. To make a long story short, we should be applying in the coming weeks. Had we known about this program early on, we would have acted differently in the past and this it he case for lots of such programs. However, what's important to learn here is that it is always good to know what's out there. For us, actively participating at &lt;a href="http://www.thecodefactory.ca"&gt;The Code Factory&lt;/a&gt; helped us get up to speed while watching &lt;a href="http://www.amazon.com/gp/product/B000JJ3Y78?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B000JJ3Y78"&gt;Arrested Development&lt;/a&gt; reruns did not.&lt;/p&gt;
&lt;p&gt;Another example is the &lt;a href="http://www.microsoft.com/BizSpark/"&gt;Microsoft BizSpark&lt;/a&gt; program that was launched this fall. It basically gives us access to free &lt;a href="http://www.microsoft.com"&gt;Microsoft&lt;/a&gt; software for three years as long as our revenue is below a certain threshold. Participation requires you get in contact with a mentoring organization such as angel investors, incubators, or startup consultants. Having met &lt;a href="http://www.flowventures.com/blog/"&gt;Quebec-based Flow Ventures&lt;/a&gt; at the first &lt;a href="http://isfanstartup.blogspot.com/2008/10/why-i-brought-founders-and-funders-to.html"&gt;Founders &amp;amp; Funders Ottawa&lt;/a&gt;, it was a good opportunity for us to begin a relationship with them. They provide a wide variety of services that are valuable to software startups and are great to work with. Thanks to &lt;a href="http://www.microsoft.com/BizSpark/"&gt;BizSpark&lt;/a&gt; and &lt;a href="http://www.flowventures.com/blog/"&gt;Flow Ventures&lt;/a&gt;, we can grow our startup with &lt;a href="http://www.microsoft.com"&gt;Microsoft&lt;/a&gt; technologies without breaking the bank (one of the main reasons &lt;a href="http://ostatic.com/blog/microsofts-biz-spark-is-another-direct-shot-at-open-source"&gt;why software engineers don't choose Microsoft technologies is because of the cost of the toolkit&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Software Tools&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.getdropbox.com/referrals/NTE0NTA3Njk"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; margin: 20px; border-right-width: 0px" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/SoftwareStartupLessonsPart4YearTwo_13C97/dropbox_3.jpg" border="0" alt="dropbox" width="244" height="170" align="right" /&gt;&lt;/a&gt; Over the course of the year, we've changed some of the tools we use for collaboration here at LavaBlast. The main tool that is worth mentioning is &lt;a href="https://www.getdropbox.com/referrals/NTE0MzcyMzk"&gt;DropBox&lt;/a&gt; for file synchronization amongst peers. We recommend it to everyone because:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Everything is synched automatically &amp;ndash; even novices can use it. &lt;/li&gt;
&lt;li&gt;It adds zero overhead to common processes &lt;/li&gt;
&lt;li&gt;It gives all the benefits of source control (revisions, restore, etc.) &lt;/li&gt;
&lt;li&gt;It is cross-platform (we use it on Windows on our dev machines, Mac OS X &lt;a href="http://www.amazon.com/gp/product/B0013FJBX8?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B0013FJBX8"&gt;on one of our laptops&lt;/a&gt;, and Ubuntu on a backup server we got for free at &lt;a href="http://www.iweb.com"&gt;iWeb Hosting&lt;/a&gt; during their February promotion). &lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.getdropbox.com/referrals/NTE0MzcyMzk"&gt;DropBox gives you 2GB for free&lt;/a&gt;, which is more than enough for most teams. (We have upgraded, however). &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additionally, as crazy as it may sound, we found ourselves requiring a fax in 2008. Yes, the rest of the world is still living in 1988. Obviously, we didn't want to get a separate landline for the eight faxes we need to send/receive a year so we decided on &lt;a href="http://www.myfax.com"&gt;MyFax&lt;/a&gt; as our email-to-fax/fax-to-email provider. Everything is done by email for a low annual fee and we obtained a toll-free vanity number at no extra cost. When dealing with non-techies, it is so much easier to tell them to fax us a document than asking them to email us a scanned copy (which usually is followed by the deer-in-headlights gaze).&lt;/p&gt;
&lt;p&gt;We also jumped on the &lt;a href="http://www.twitter.com"&gt;Twitter&lt;/a&gt; bandwagon last summer, after integrating &lt;a href="http://www.thecodefactory.ca"&gt;The Code Factory&lt;/a&gt; with &lt;a href="http://www.twitter.com"&gt;Twitter&lt;/a&gt;. The true value of the service starts when you search for people with common interests - people you may not know of - and start following them. Following &lt;a href="http://www.tigerdirect.com"&gt;TigerDirect&lt;/a&gt; allowed us to land a good deal on an uninterruptible power supply (We asked TigerDirect to put a product on promotion.. and they did!). Follow &lt;a href="http://twitter.com/jkealey"&gt;Jason&lt;/a&gt; and &lt;a href="http://twitter.com/EtienneT"&gt;Etienne&lt;/a&gt; on Twitter, after watering your plants, if you have nothing better to do.&lt;/p&gt;
&lt;p&gt;Finally, we started using &lt;a href="http://www.rescuetime.com/"&gt;RescueTime&lt;/a&gt; over a year ago. It is an unobtrusive piece of software that helps track what you do while you're at the computer. Most software is already tagged by the community, so you don't spend a week classifying events - &lt;em&gt;unless you want to&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Hardware Tools&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://embody.hermanmiller.com/"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; margin: 10px; border-right-width: 0px" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/SoftwareStartupLessonsPart4YearTwo_13C97/embody_3.png" border="0" alt="embody" width="211" height="283" align="right" /&gt;&lt;/a&gt;Not only is our company two years old... and so are our computers. Software engineers only require three things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A fast computer with a couple screens &lt;/li&gt;
&lt;li&gt;A comfortable chair and desk &lt;/li&gt;
&lt;li&gt;An endless supply of caffeinated beverages &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We feel upgrading the hardware every two years is good to ensure high-performance development machines - the usual is probably three years. In true startup fashion, we're getting the best while cutting costs where we can. We're building the computers ourselves and reusing our old &lt;a href="http://www.amazon.com/gp/product/B000I5JHB0?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B000I5JHB0"&gt;Antec computer case&lt;/a&gt;, &lt;a href="http://www.amazon.com/gp/product/B000BM2PUA?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B000BM2PUA"&gt;power supply&lt;/a&gt;, 1TB hard drives, video cards, DVD-RW, etc. Here's what we're getting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/B001H5T7LK?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B001H5T7LK"&gt;Core i7 920 Processor&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/B001P61O4Y?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B001P61O4Y"&gt;Asus P6T Deluxe V2&lt;/a&gt; (Up to 24GB RAM ... etc) &lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/B001NFZ5PQ?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B001NFZ5PQ"&gt;12 GB RAM&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/B001F4YIYY?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=B001F4YIYY"&gt;Solid State Drive&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Furthermore, we decided to follow &lt;a href="http://www.joelonsoftware.com/articles/FieldGuidetoDevelopers.html"&gt;Joel Spolsky's advice and get some fancy chairs, as we'll be using them for the next decade&lt;/a&gt;. Goodbye crappy &lt;a href="http://www.staples.ca"&gt;Business Depot&lt;/a&gt; chairs - hello LavaBlast branded &lt;a href="http://embody.hermanmiller.com/"&gt;Herman Miller replacements&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;Having a blog helps: a concrete example&lt;/h2&gt;
&lt;p&gt;The conclusion of &lt;a href="http://blog.lavablast.com/post/2008/03/Software-Startup-Lessons-(Part-3)---Marketing2c-Sales-amp3b-Growth.aspx"&gt;Part 3&lt;/a&gt; in our series discussed co-working as a great way to meet other people. At the time of writing, there were no co-working locations in Ottawa. After publishing our third post, &lt;a href="http://www.startupottawa.com/?p=225"&gt;StartupOttawa.com&lt;/a&gt; picked up our articles and promoted us as one of the &lt;a href="http://www.startupottawa.com/?p=224"&gt;local start-ups&lt;/a&gt;. At the same time, &lt;a href="http://www.klondikeconsulting.com/"&gt;Ian Graham was putting his business plan into action&lt;/a&gt;. For over a year, Ian had been planning to open a &lt;a href="http://www.thecodefactory.ca"&gt;co-working location in Ottawa&lt;/a&gt;. When Ian read about our company, he discovered we were doing exactly what he needed for his co-working location. A few months later, &lt;a href="http://blog.lavablast.com/post/2008/04/Co-working-environments-are-good-for-software-startups.aspx"&gt;The Code Factory launched featuring LavaBlast's software solution&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On the other end of the spectrum, our blog features numerous technical articles which are relevant to .NET developers worldwide. We've submitted most of our articles to a community-based aggregator called &lt;a href="http://www.dotnetkicks.com"&gt;DotNetKicks&lt;/a&gt;. Our best posts were selected by the crowd and referenced by other bloggers worldwide, increasing our &lt;a href="http://en.wikipedia.org/wiki/PageRank"&gt;Google PageRank&lt;/a&gt;. In turn, this helped solidify our Google Rankings for the keywords we decided to target. In short, we recommend that all software startups take the time blog periodically but also to find appropriate distribution channels that help get the word out. Telling your mother doesn't count.&lt;/p&gt;
&lt;p&gt;However, even if the blog is a great tool, it doesn&amp;rsquo;t beat the face-to-face interactions one can have at a local incubator, &lt;a href="http://www.thecodefactory.ca"&gt;co-working location&lt;/a&gt;, or &lt;a href="http://foundersandfunders.org/2008/10/01/founders-funders-ottawa/"&gt;founders &amp;amp; funders&lt;/a&gt; event. Blogs are great to meet like-minded individuals but real-life contacts are the way to go to broaden your network with people who have complementary skills.&lt;/p&gt;
&lt;p&gt;Come back next week for &lt;a href="http://blog.lavablast.com/post/2009/04/Software-Startup-Lessons-(Part-5)-ndash3b-Being-a-software-startup-in-a-recession.aspx"&gt;Part 5: Being a software startup in a recession&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2009%2f03%2fSoftware-Startup-Lessons-(Part-4)-ndash3b-Year-Two.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2009%2f03%2fSoftware-Startup-Lessons-(Part-4)-ndash3b-Year-Two.aspx&amp;amp;border=660000&amp;amp;fgcolor=660000&amp;amp;bgcolor=FF9900&amp;amp;cbgcolor=FFFF00" border="0" alt="kick it on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/J7a0wAh8Of8" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/J7a0wAh8Of8/post.aspx</link>
      <author>JKealey</author>
      <comments>http://blog.lavablast.com/post/2009/03/30/Software-Startup-Lessons-(Part-4)-ndash3b-Year-Two.aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=e09afe4a-d19d-4070-8a41-ebb24e1c8dd5</guid>
      <pubDate>Mon, 30 Mar 2009 14:31:00 -0500</pubDate>
      <category>Business</category>
      <category>General</category>
      <category>News</category>
      <dc:publisher>JKealey</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=e09afe4a-d19d-4070-8a41-ebb24e1c8dd5</pingback:target>
      <slash:comments>5</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=e09afe4a-d19d-4070-8a41-ebb24e1c8dd5</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2009/03/30/Software-Startup-Lessons-(Part-4)-ndash3b-Year-Two.aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=e09afe4a-d19d-4070-8a41-ebb24e1c8dd5</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=e09afe4a-d19d-4070-8a41-ebb24e1c8dd5</feedburner:origLink></item>
    <item>
      <title>Gotchas: Migration from IIS6 + SQL 2005 (32-bit) to IIS7 + SQL 2008 (64-bit)</title>
      <description>&lt;p&gt;
&lt;a href="http://www.flickr.com/photos/jpdaigle/3112044471/"&gt;&lt;img style="border-width: 0px; margin: 10px" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/GotchasMigrationfromIIS6SQL200532bittoII_1025C/sacacomie_3.jpg" border="0" alt="LavaBlast Holiday Retreat" width="359" height="240" align="right" /&gt;&lt;/a&gt; First of all, let me wish you a Happy New Year! 
&lt;/p&gt;
&lt;p&gt;
Over the past couple weeks, we&amp;#39;ve been upgrading our server to a fresh install of Windows Server 2008 64-bit with more RAM and faster drives than our old Server 2003 32-bit. To make a long story short, this upgrade was much more painful than the one we did two years ago, which went flawlessly. I experienced so many pain points that, had I written them all down, you would have a 10 page blog post to read! I&amp;#39;ll list a few things here that I remember off the top of my head (the main issues), hoping this will help some of you out there.&amp;nbsp; 
&lt;/p&gt;
&lt;h2&gt;IIS 6 32-bit to IIS 7 64-bit &lt;/h2&gt; 
&lt;ul&gt;
	&lt;li&gt;Problem: Migrating the IIS configuration. Our metabase backups were of no use because we were upgrading IIS at the same time.  
	&lt;ul&gt;
		&lt;li&gt;Solution: We used &lt;a href="http://www.iis.net/extensions/WebDeploymentTool"&gt;msdeploy&lt;/a&gt; to migrate our IIS.  &lt;/li&gt;
		&lt;li&gt;Note: We didn&amp;#39;t use msdeploy to migrate our files or databases, as we had already &lt;a href="http://en.wikipedia.org/wiki/Rsync" target="_blank"&gt;rsync&lt;/a&gt;&amp;#39;ed these over to the new server (~30 gigabytes). &lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
	&lt;li&gt;Problem: Our application pools ended up being configured as 32-bit instead of 64-bit. Our sites wouldn&amp;#39;t load.  
	&lt;ul&gt;
		&lt;li&gt;Solution: We had to change the &lt;a href="http://blogs.msdn.com/rakkimk/archive/2007/11/03/iis7-running-32-bit-and-64-bit-asp-net-versions-at-the-same-time-on-different-worker-processes.aspx"&gt;app pool configuration manually&lt;/a&gt;.  &lt;/li&gt;
		&lt;li&gt;Note: &lt;a href="http://twitter.com/bstaples"&gt;Bill Staples&lt;/a&gt; revealed I ended up downloading the wrong version of the tool (beta1 instead of beta2). &lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
	&lt;li&gt;Problem: Our applications pools ended up trying to use accounts from the old machine instead of the new one.  
	&lt;ul&gt;
		&lt;li&gt;Solution: We changed these manually.  &lt;/li&gt;
		&lt;li&gt;Note: Probably related to the same issue as above. &lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
	&lt;li&gt;Problem: I had to install ASP.NET 1.1 for one of our customers. This and/or the previous tool ended up screwing up our IIS Handler mapping configuration. The applications were trying to use either the ASP.NET 1.1 or the 32-bit ASP.NET 2.0 dlls (can&amp;#39;t remember which).  
	&lt;ul&gt;
		&lt;li&gt;Solution: Not knowing of a better way, I ended up manually changing the defaults for the whole web server to (C:\Windows\Microsoft.NET\Framework64\v2.0.50727\aspnet_filter.dll) and using the &amp;quot;Revert to inherited&amp;quot; feature of IIS7 Manager for all our apps.  &lt;/li&gt;
		&lt;li&gt;Note: I still don&amp;#39;t know what the best practices are for installing ASP.NET 1.1 after a higher ASP.NET is installed, without breaking everything. &lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
	&lt;li&gt;Problem: IIS7 screwed around with the formatting of my (dozens of) web.config files. We need to figure out what changed and bring these back into  
	&lt;ul&gt;
		&lt;li&gt;Solution: &lt;a href="http://winmerge.org/"&gt;WinMerge&lt;/a&gt; and &lt;a href="http://tortoisesvn.net/"&gt;TortoiseSVN&lt;/a&gt; to track changes to figure out what to replicate into the local &lt;a href="http://blog.lavablast.com/post/2008/02/Manage-your-ASPNET-Webconfig-Files-using-NAnt.aspx"&gt;Web.config templates that we use to auto-generate configuration files&lt;/a&gt;.  &lt;/li&gt;
		&lt;li&gt;Note: Not fun! Some things are obvious (changed connection strings, but others are harder to find). &lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
	&lt;li&gt;Problem: Some web services that we use in our scripts were returning illegal protocol errors. We had to add the following code in our Web.config to re-enable GET and POST. &lt;/li&gt;
&lt;/ul&gt;
&lt;div class="csharpcode"&gt;
&lt;pre class="alt"&gt;
&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;webServices&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;    
&lt;/pre&gt;
&lt;pre&gt;
    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;protocols&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;        
&lt;/pre&gt;
&lt;pre class="alt"&gt;
        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;add&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;HttpGet&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;       
&lt;/pre&gt;
&lt;pre&gt;
        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;add&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;HttpPost&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;    
&lt;/pre&gt;
&lt;pre class="alt"&gt;
    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;protocols&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;pre&gt;
&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;webServices&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;SQL Server 2005 32-bit to SQL Server 2008 64-bit &lt;/h2&gt;
&lt;ul&gt;
	&lt;li&gt;Problem: Reloading the databases on the new server via the command line. 
	&lt;ul&gt;
		&lt;li&gt;Solution: We restored our databases from the command line following &lt;a href="http://blog.lavablast.com/post/2008/10/SQL-Server-Restore-a-bak-to-a-database-in-command-line.aspx"&gt;our previous blog post&lt;/a&gt;. 
		&lt;/li&gt;
		&lt;li&gt;Note: However, we had to make a &lt;a href="http://blog.lavablast.com/demos/SqlServer2008_RestoreFromCommandLine.zip"&gt;few changes for the scripts to work for SQL Server 2008&lt;/a&gt;. 
		&lt;/li&gt;
		&lt;li&gt;Note 2: We did notice that the databases were upgraded when reloaded. If backed up and sent back to a machine running SQL Server 2005, these cannot be reloaded.&amp;nbsp; &lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
	&lt;li&gt;Problem: ASP.NET could not access the imported databases. 
	&lt;ul&gt;
		&lt;li&gt;Solution: We had to manually fix the security configuration for each database on the target machine so that ASP.NET could access it. 
		&lt;/li&gt;
		&lt;li&gt;Note: I wonder if this is due to the way we are restoring our databases. &lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
	&lt;li&gt;Problem: We had SQL Server Express installed. One of our demo applications used a connection string such as &amp;quot;Data Source=.\SQLEXPRESS;&lt;strong&gt;AttachDbFilename=|DataDirectory|\NORTHWND.MDF&lt;/strong&gt;;Integrated Security=True;&lt;strong&gt;User Instance=True&lt;/strong&gt;&amp;quot;. The full-blown version of SQL Server doesn&amp;#39;t support user instances. 
	&lt;ul&gt;
		&lt;li&gt;Solution: We restored the database into SQL Server and are using it instead of the User Instance. 
		&lt;/li&gt;
		&lt;li&gt;Note: I suppose you could also install the express version side-by-side if you wanted to. &lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
	&lt;li&gt;Problem: We installed SQL Server Reporting Services (SSRS). By default, it binds to the ~/reports of all our websites, breaking a couple of our sites that were using the ~/reports subfolder. (It also binds to the ~/reportserver folder, but we weren&amp;#39;t using that one). 
	&lt;ul&gt;
		&lt;li&gt;Solution: We had to launch the SSRS manager to change the virtual directory that was used. &lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
	&lt;li&gt;Problem: Some of our older applications used the SQLNCLI provider, but these now return &amp;quot;&lt;strong&gt;System.InvalidOperationException: The &amp;#39;SQLNCLI&amp;#39; provider is not registered on the local machine.&lt;/strong&gt;&amp;quot;. 
	&lt;ul&gt;
		&lt;li&gt;Solution: Installing the client (from the SQL Server 2008 DVD) only ends up creating a new provider &amp;quot;SQLNCLI10&amp;quot; that we can use in our connection strings instead of installing the older version. 
		&lt;/li&gt;
		&lt;li&gt;Solution 2: We noticed that some code using SQLNCLI10 did not work properly. &lt;em&gt;System.Data.OleDb.OleDbException: The fractional part of the provided time value overflows the scale of the corresponding SQL Server parameter or column. Increase bScale in DBPARAMBINDINFO or column scale to correct this error. &lt;/em&gt;We ended up changing providers, as this was too much of a pain. &lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
	&lt;li&gt;Problem: We use automatic script generation in SQL Server Management Studio (SSMS). &lt;em&gt;Tools ==&amp;gt; Options ==&amp;gt; Designers ==&amp;gt; Table and Database Designers ==&amp;gt; Auto Generate Change Scripts. &lt;/em&gt;However, the scripts that are now generated in our local SQL2k8 databases don&amp;#39;t always work with the SQL2k5 databases we have deployed in the wild. We&amp;#39;d like the scripts to be compatible with both, so that we don&amp;#39;t have to manually remove the problematic portions. 
	&lt;ul&gt;
		&lt;li&gt;Solution: &lt;a href="http://stackoverflow.com/questions/428909/sql-server-2008-auto-generate-change-scripts-legacy-problem"&gt;Stack Overflow helped us find a solution&lt;/a&gt;!&lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
	&lt;li&gt;Problem: SQL Server 2005 cannot open our SQL Server 2008 backups, even if their compatibility mode appears to be 2005. 
	&lt;ul&gt;
		&lt;li&gt;Solution: I don&amp;#39;t know of a solution, do you? :)&lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
Most of our other applications were transferred easily, except for &lt;a href="http://www.twiki.org"&gt;TWiki&lt;/a&gt; which is always a pain to install on a Windows-based server. We still haven&amp;#39;t resolved a CSS issue, but the tool now works! I hope this helps! Best of luck to you all in 2009! Feel free to comment about the problems you experienced during your recent migrations to these technologies. 
&lt;/p&gt;
&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2009%2f01%2fGotchas-Migration-from-IIS6-2b-SQL-2005-(32-bit)-to-IIS7-2b-SQL-2008-(64-bit).aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2009%2f01%2fGotchas-Migration-from-IIS6-2b-SQL-2005-(32-bit)-to-IIS7-2b-SQL-2008-(64-bit).aspx&amp;amp;border=660000&amp;amp;fgcolor=660000&amp;amp;bgcolor=FF9900&amp;amp;cbgcolor=FFFF00" border="0" alt="kick it on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/1u_aUCVQBOE" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/1u_aUCVQBOE/post.aspx</link>
      <author>JKealey</author>
      <comments>http://blog.lavablast.com/post/2009/01/12/Gotchas-Migration-from-IIS6-2b-SQL-2005-(32-bit)-to-IIS7-2b-SQL-2008-(64-bit).aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=193faf00-8f52-4567-ad36-1d26f59c2e68</guid>
      <pubDate>Mon, 12 Jan 2009 10:47:00 -0500</pubDate>
      <category>Software</category>
      <dc:publisher>JKealey</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=193faf00-8f52-4567-ad36-1d26f59c2e68</pingback:target>
      <slash:comments>12</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=193faf00-8f52-4567-ad36-1d26f59c2e68</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2009/01/12/Gotchas-Migration-from-IIS6-2b-SQL-2005-(32-bit)-to-IIS7-2b-SQL-2008-(64-bit).aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=193faf00-8f52-4567-ad36-1d26f59c2e68</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=193faf00-8f52-4567-ad36-1d26f59c2e68</feedburner:origLink></item>
    <item>
      <title>Co-Working Joliette</title>
      <description>&lt;p&gt;
&lt;a href="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/CoWorkingJoliette_8631/image_2.png"&gt;&lt;img style="border-width: 0px; margin: 0px 0px 10px 10px; display: inline" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/CoWorkingJoliette_8631/image_thumb.png" border="0" alt="image" title="image" width="288" height="217" align="right" /&gt;&lt;/a&gt; Last night I had the pleasure to assist to the official opening of the &lt;a href="http://www.coworking-joliette.com/" target="_blank"&gt;new co-working space opened here in Joliette&lt;/a&gt;.&amp;nbsp; The mayor of Joliette M. Ren&amp;eacute; Laurin was there to officially launch the project along with eighty people who were there to visit, drink wine, and exchange business cards.&amp;nbsp; The new co-working space is located right on top of Librairie Ren&amp;eacute; Martin in downtown Joliette.
&lt;/p&gt;
&lt;p&gt;
We want to encourage this kind of project because we think it encourage entrepreneurship here in Joliette.&amp;nbsp; It enables small companies to have an affordable office space and enables entrepreneurs to meet new people with different work specialities.
&lt;/p&gt;
&lt;p&gt;
Readers who have followed our blog for some time now know that we have done the software for &lt;a href="http://www.thecodefactory.ca/" target="_blank"&gt;The Code Factory&lt;/a&gt;, a co-working space for software startups in Ottawa.&amp;nbsp; &lt;a href="http://www.coworking-joliette.com/" target="_blank"&gt;Co-Working Joliette&lt;/a&gt; has a different approach than &lt;a href="http://www.thecodefactory.ca/" target="_blank"&gt;The Code Factory&lt;/a&gt; for its pricing model and doesn&amp;#39;t focus exclusively on software startups.&amp;nbsp; Joliette (~100,000 inhabitants) is not Ottawa or Montreal, so it doesn&amp;rsquo;t have the affluence of new people that those two cities may have, but Joliette still has many entrepreneurs who need office space and want to meet new people.&amp;nbsp; For this reason, entrepreneurs can work at Co-Working Joliette by purchasing a daily pass or rent office space on a monthly or yearly basis.&amp;nbsp; You can have your own closed office or share a work table with other people.&amp;nbsp; You can access your office 24/7, have a free parking space, and, of course, unlimited Internet access. 
&lt;/p&gt;
&lt;p&gt;
According to &lt;a href="http://www.benoit-grenier.com/" target="_blank"&gt;Benoit Grenier&lt;/a&gt; of &lt;a href="http://www.developpement8020.com/" target="_blank"&gt;Communication 8020&lt;/a&gt;, this is the second co-working space in the province of Quebec.&amp;nbsp; They already have partnerships with two other co-working spaces: one in New York, the &lt;a href="http://www.nwcny.com/blog/" target="_blank"&gt;New Worker&lt;/a&gt;, and one in Vancouver, &lt;a href="http://www.abetterplacetowork.com/"&gt;WorkSpace&lt;/a&gt;.&amp;nbsp; They are in talks with a co-working space in London right now but nothing has been signed as of yet.
&lt;/p&gt;
&lt;p&gt;
To me, the opening of a co-working location in a smaller community confirms what we&amp;#39;ve been claiming on our blog for the past two years. The Internet has made it possible to have clients all over the world, regardless of your company size and location. We&amp;#39;ve seen this first hand in the software world, but it also applies to firms specializing in marketing, translation, etc. 
&lt;/p&gt;
&lt;p&gt;
Good luck to all involved and let&amp;rsquo;s hope this project becomes a success in Joliette.
&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/a93fwxuL67s" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/a93fwxuL67s/post.aspx</link>
      <author>EtienneT</author>
      <comments>http://blog.lavablast.com/post/2009/01/08/Co-Working-Joliette.aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=a0d344ba-b0d2-4647-a7b6-ab840d88de86</guid>
      <pubDate>Thu, 08 Jan 2009 10:16:00 -0500</pubDate>
      <dc:publisher>EtienneT</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=a0d344ba-b0d2-4647-a7b6-ab840d88de86</pingback:target>
      <slash:comments>3</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=a0d344ba-b0d2-4647-a7b6-ab840d88de86</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2009/01/08/Co-Working-Joliette.aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=a0d344ba-b0d2-4647-a7b6-ab840d88de86</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=a0d344ba-b0d2-4647-a7b6-ab840d88de86</feedburner:origLink></item>
    <item>
      <title>Declare your ObjectDataSource using lambda expressions</title>
      <description>&lt;p&gt;&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://blog.lavablast.com/ODSTyped/Example1-ShowAll.aspx" target="_blank"&gt;Example Page with Northwind&lt;/a&gt; | &lt;a href="http://blog.lavablast.com/ODSTyped/LavaBlast.ODS.Web.zip" target="_blank"&gt;Download Source&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Because &lt;a href="http://www.subsonicproject.com"&gt;SubSonic&lt;/a&gt; generates the business objects and &lt;a href="http://blog.lavablast.com/post/2007/11/SubSonic-magic.aspx"&gt;the controllers in our data access layer&lt;/a&gt;, I often bind my controls to an ObjectDataSource. I love the ObjectDataSource but one of the most annoying things with it is that you must declare your select/update/delete method names as strings. The ObjectDataSource will call these methods via reflection, as the compiler doesn't have an explicit reference to the methods being invoked.&amp;#160; I found a solution to be able to set all the required properties of an ObjectDataSource simply by making a fake call on a lambda expression. Keep reading if you want to know how this works.&amp;#160; Simply set your SelectMethod like this: &lt;/p&gt;  &lt;div class="csharpcode"&gt;   &lt;pre class="alt"&gt;ods.SetSelectMethod&amp;lt;ProductController&amp;gt;(ctrl =&amp;gt; ctrl.FetchAll());&lt;/pre&gt;
&lt;/div&gt;

&lt;h2&gt;What are the issues with declaring names using strings?&lt;/h2&gt;

&lt;p&gt;Here is a typical ObjectDataSource declaration in your ASPX/ASCX: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:ObjectDataSource&lt;/span&gt; &lt;span class="attr"&gt;ID&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;sqlDataSource&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;runat&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;server&amp;quot;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="attr"&gt;SelectMethod&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;FetchByProductAuthority&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;TypeName&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;Generated.StoreAuthority.ItemGroupController&amp;quot;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="attr"&gt;EnablePaging&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;True&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;SortParameterName&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;sort&amp;quot;&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;SelectParameters&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:Parameter&lt;/span&gt; &lt;span class="attr"&gt;Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;pa&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;String&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:Parameter&lt;/span&gt; &lt;span class="attr"&gt;Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;search&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;String&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;asp:Parameter&lt;/span&gt; &lt;span class="attr"&gt;Name&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;user&amp;quot;&lt;/span&gt; &lt;span class="attr"&gt;Type&lt;/span&gt;&lt;span class="kwrd"&gt;=&amp;quot;String&amp;quot;&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;    &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;SelectParameters&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;asp:ObjectDataSource&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;br /&gt;Why is it bad to have those properties set declaratively as strings, especially SelectMethod and TypeName?&amp;#160; If you ever rename your class, the select method or even add/remove its parameters, you will never know at compile time that you have broken your ODS (ObjectDataSource). Code refactoring tools don't affect these declarations either, so you are stuck using Find and Replace to change the strings in your declarations. In the worst case, you'll discover at run-time that you've broken your application. 

&lt;h2&gt;Expressions + Lambda to the rescue&lt;/h2&gt;

&lt;p&gt;I just recently learned about Expression&amp;lt;T&amp;gt; from &lt;a href="http://www.codethinked.com/post/2008/11/29/Taking-the-Magic-out-of-Expression.aspx" target="_blank"&gt;this article&lt;/a&gt; and it gave me an idea.&amp;#160; I managed to make something work with an extension method on the ObjectDataSource class plus a bit of extra glue logic that I will cover today.&amp;#160; My solution is not perfect since it’s the first time I play with Expressions, but I think it’s a good start.&amp;#160; I'd be ecstatic if you wanted to help me improve the syntax! &lt;/p&gt;

&lt;p&gt;Here is how you would set your SelectMethod, TypeName and select parameters on an ODS, using my newly created extension method SetSelectMethod. &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;ods.SetSelectMethod&amp;lt;ProductController&amp;gt;(ctrl =&amp;gt; ctrl.FetchAll());&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&amp;#160; &lt;/p&gt;

&lt;p&gt;&lt;a href="http://blogs.msdn.com/charlie/archive/2008/02/13/linq-farm-seed-using-the-expression-tree-visualizer.aspx"&gt;&lt;img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; margin: 10px 10px 10px 0px; border-right-width: 0px" height="263" alt="image" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/TypedSelectmethodforanObjectDataSource_DA1A/image_3.png" width="278" align="left" border="0" /&gt;&lt;/a&gt;What is happening in this piece of code?&amp;#160; We are calling an extension method on ObjectDataSource.&amp;#160; This extension method takes an Expression&amp;lt;Action&amp;lt;T&amp;gt;&amp;gt; as a parameter, where T is our controller in the &lt;a href="http://en.wikipedia.org/wiki/Model-view-controller"&gt;model-view-controller pattern&lt;/a&gt;.&amp;#160; This basically means that we can pass an expression tree to the method that will be analyzed to figure out the information we're looking for. If you’re not familiar with what I’m saying, I recommend you take a look at this great article: &lt;a href="http://www.codethinked.com/post/2008/11/29/Taking-the-Magic-out-of-Expression.aspx" target="_blank"&gt;Taking the magic out of Expression&lt;/a&gt;.&amp;#160; The expression tree enable us to retrieve all kinds of information from the lambda expression, like the TypeName (in this case “LavaBlast.Data.ProductController”) of the object used to make the method call and the Method name itself (SelectMethod = “FetchAll”).&amp;#160; We can even parse each parameter used and add them to the parameter collection of the ODS.&amp;#160; We can give Parameters default values derived from our expression tree of the lambda expression.&amp;#160; More on that later. &lt;/p&gt;

&lt;p&gt;The figure on the left represents the expression tree for the lambda “ctrl =&amp;gt; ctrl.FetchAll()”. &lt;/p&gt;

&lt;p&gt;The goal of the lambda expression you pass to SetSelectMethod&amp;lt;T&amp;gt; is to make a &lt;strong&gt;fake call&lt;/strong&gt; to the method you want the ODS to use when they make a Select operation.&amp;#160; By fake call I mean the ODS won’t invoke this lambda expression because it is there only to get analyzed for information, like the object name, method name, parameters, etc. However, since we are making a valid method call with all required parameters, you will be able to see &lt;strong&gt;at compile time&lt;/strong&gt; that refactored code has affected this ODS. If we ever change this method in the future and it doesn’t compile anymore, then we’ll know it here. &lt;/p&gt;

&lt;p&gt;It’s time for a real life example of something useful.&amp;#160; The last piece of code didn’t even include sorting and paging. Here is an example setting our ODS to utilize sorting and paging. Note that because paging is enabled, a SelectCountMethod is required by our ODS: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;ods.SetSelectMethod&amp;lt;ProductController&amp;gt;(&lt;/pre&gt;

  &lt;pre&gt;    ctrl =&amp;gt; ctrl.FetchAll(String.Empty, 0, 0),&lt;/pre&gt;

  &lt;pre class="alt"&gt;    ctrl =&amp;gt; ProductController.GetCount());&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;br /&gt;The method called has the following signature: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; ProductCollection FetchAll(&lt;span class="kwrd"&gt;string&lt;/span&gt; sort, &lt;span class="kwrd"&gt;int&lt;/span&gt; startRowIndex, &lt;span class="kwrd"&gt;int&lt;/span&gt; maximumRows)&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;br /&gt;This time we have two lambda expressions, one with three empty arguments (these default values are ignored but the data types help us identify a particular method) and one defining our GetCount method.&amp;#160; The second one references a static method, but is very similar to what we've already covered. The properties TypeName, SelectMethod and SelectCount on the ODS will be specified by our SetSelectMethod. &lt;/p&gt;

&lt;h2&gt;Analyzing Parameters&lt;/h2&gt;

&lt;p&gt;What if we want to add parameters coming from the Session or from the value of a control on the page?&amp;#160; Or simply a Parameter having a default value?&amp;#160; The expression tree can be analyzed to discover the parameters to be added to our SelectParameter collection. &lt;/p&gt;

&lt;p&gt;First of all, I’ll begin with a simple Parameter which has a constant default value.&amp;#160; Here is an example of a method filtering results depending on the logged in user's username: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;ods.SetSelectMethod&amp;lt;ProductController&amp;gt;(ctrl =&amp;gt; ctrl.FetchAll(HttpContext.Current.User.Identity))&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;br /&gt;The method declaration for this FetchAll: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; C FetchAll(&lt;span class="kwrd"&gt;string&lt;/span&gt; user)&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;br /&gt;Imagining the currently logged in user is called &amp;quot;Etienne&amp;quot;, we want to insert a Parameter with a default value of “Etienne”.&amp;#160; We do that by executing the value of the first parameter (HttpContext.Current.User.Identity), which will return “Etienne” and then when we create the parameter for which we set the default value.&amp;#160; We would create a parameter like this: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;var parameter = &lt;span class="kwrd"&gt;new&lt;/span&gt; Parameter(&lt;span class="str"&gt;&amp;quot;user&amp;quot;&lt;/span&gt;, DbType.String, &lt;span class="str"&gt;&amp;quot;Etienne&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;br /&gt;Now this was for a parameter with constant values, but what if the values comes from the Session (SessionParameter) or a Control value (ControlParameter)? &lt;/p&gt;

&lt;p&gt;For example, in a page, I have a TextBox named txtSearch.&amp;#160; It's value can be used to filter the results in a GridView according to search terms.&amp;#160; We can't pass txtSearch.Text as above, as this would invoke it immediately instead of informing the ODS to query the control every time it fetches the data (it would become a Parameter instead of SessionParameter). However, we can pass a special type of parameter which will be recognized by our expression parser: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;ods.SetSelectMethod&amp;lt;ProductController&amp;gt;(&lt;/pre&gt;

  &lt;pre&gt;    ctrl =&amp;gt; ctrl.FetchAll(    ODSHelper.Control&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;(() =&amp;gt; txtSearch.Text),&lt;/pre&gt;

  &lt;pre class="alt"&gt;                            String.Empty, 0, 0),&lt;/pre&gt;

  &lt;pre&gt;    ctrl =&amp;gt; ctrl.GetCount(String.Empty));&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;br /&gt;The related method declaration for FetchAll is as follow: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;virtual&lt;/span&gt; ProductCollection FetchAll(&lt;span class="kwrd"&gt;string&lt;/span&gt; search, &lt;span class="kwrd"&gt;string&lt;/span&gt; sort, &lt;span class="kwrd"&gt;int&lt;/span&gt; startRowIndex, &lt;span class="kwrd"&gt;int&lt;/span&gt; maximumRows)&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;br /&gt;Here the main difference&amp;#160; is the call to “ODSHelper.Control&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;(() =&amp;gt; txtSearch.Text)”&amp;#160; as the first parameter to &lt;em&gt;FetchAll&lt;/em&gt;.&amp;#160; At this point, we have to remember that this is a &lt;strong&gt;fake call&lt;/strong&gt; to our method &lt;em&gt;FetchAll&lt;/em&gt;.&amp;#160; The method call ODSHelper.Control&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; takes a lambda representing which control and which property on this control should be used while constructing the ControlParameter that will be added to the SelectParameter collection. From the information contained in ODSHelper.Control&amp;lt;string&amp;gt;(() =&amp;gt; txtSearch.Text), we can construct a ControlParameter as follows and insert it into the ODS SelectParameter collection: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;var parameter = &lt;span class="kwrd"&gt;new&lt;/span&gt; ControlParameter(&lt;span class="str"&gt;&amp;quot;search&amp;quot;&lt;/span&gt;, DbType.String, &lt;span class="str"&gt;&amp;quot;txtSearch&amp;quot;&lt;/span&gt;, &lt;span class="str"&gt;&amp;quot;Text&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;br /&gt;The first generic parameter T we pass in the method ODSHelper.Control&amp;lt;T&amp;gt; is simply the return type.&amp;#160; We want our lambda to compile even if it’s a fake call, so we simply make ODSHelper.Control&amp;lt;T&amp;gt; return an object of type T, which is the data type of the parameter on our SelectMethod (in this case a string). &lt;/p&gt;

&lt;h2&gt;Analyzing a Lambda Expression&lt;/h2&gt;

&lt;p&gt;From this point forward, the article raises the geekiness bar even higher, as we drill-down into how all of this is implemented in the background. If you are interested how we analyze the expression tree, continue to read. &lt;/p&gt;

&lt;p&gt;Let’s look at what we are doing inside the extension method SetSelectMethod&amp;lt;T&amp;gt;(Expression&amp;lt;Action&amp;lt;T&amp;gt;&amp;gt; exp).&amp;#160; I strongly suggest you download the project at the end of the article to see all the code file at once, but I’ll paste and explain the most important parts here. &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; SetSelectMethod&amp;lt;T&amp;gt;(&lt;span class="kwrd"&gt;this&lt;/span&gt; ObjectDataSource ds, Expression&amp;lt;Action&amp;lt;T&amp;gt;&amp;gt; exp, Expression&amp;lt;Action&amp;lt;T&amp;gt;&amp;gt; expCount)&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;br /&gt;This is the declaration of our extension method.&amp;#160; We are extending ObjectDataSource and we need an Expression for the select method and one for the GetCount method. Since we are using an Action&amp;lt;T&amp;gt; this means the lambda expression doesn’t need to return anything.&amp;#160; We shall now dissect this method in greater detail. &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&lt;span class="rem"&gt;// Make sure we have a lambda expression in the right format&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;var lambda = (LambdaExpression)exp;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; (lambda.Body.NodeType != ExpressionType.Call)&lt;/pre&gt;

  &lt;pre&gt;    &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; InvalidOperationException(&lt;span class="str"&gt;&amp;quot;Expression must be a call expression.&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;var call = (MethodCallExpression)lambda.Body;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="rem"&gt;// Our lambda needs to be a call to a method&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;if&lt;/span&gt; (call.Method == &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;

  &lt;pre&gt;    &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; InvalidOperationException(&lt;span class="str"&gt;&amp;quot;Expression must be a method reference.&amp;quot;&lt;/span&gt;);&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&amp;#160; &lt;/p&gt;

&lt;p&gt;&amp;#160; &lt;br /&gt;Those first lines simply make sure that our lambda expression is indeed a method call on an object and that we have a valid method too.&amp;#160; Just from the information we have so far from the expression tree, we can already set the TypeName and SelectMethod of the ODS: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;ds.TypeName = call.Object.Type.FullName;&lt;/pre&gt;

  &lt;pre&gt;ds.SelectMethod = call.Method.Name;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&amp;#160; &lt;/p&gt;

&lt;p&gt;&amp;#160; &lt;br /&gt;The interesting part lies in the fact that we can analyze the parameters of the method call and extract parameters the ODS needs to make the call to SelectMethod.&amp;#160; The loop to analyze the parameters is quite big.&amp;#160; I’ll put it all here and explain what is happening directly in code comments.&amp;#160; The most important thing to understand before reading the code is that if we encounter a method call in one of the parameters, we have to check to see if it’s one of the special method call like ODSHelper.Control or ODSHelper.Session.&amp;#160; We do that by checking if the method have a special attribute applied to them.&amp;#160; If the attribute is not there, we simply execute the method so that it returns a value and we use this value as the default value of the parameter.&amp;#160; Here is the code for the loop: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;int&lt;/span&gt; i = 0;&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="rem"&gt;// Then from the method call reflection information, we can get the parameters&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;foreach&lt;/span&gt; (var item &lt;span class="kwrd"&gt;in&lt;/span&gt; call.Method.GetParameters())&lt;/pre&gt;

  &lt;pre&gt;{&lt;/pre&gt;

  &lt;pre class="alt"&gt;    &lt;span class="rem"&gt;// Don't add any parameters that are standard fields like sort and paging fields&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (ds.SortParameterName != item.Name&lt;/pre&gt;

  &lt;pre class="alt"&gt;        &amp;amp;&amp;amp; ds.StartRowIndexParameterName != item.Name&lt;/pre&gt;

  &lt;pre&gt;        &amp;amp;&amp;amp; ds.MaximumRowsParameterName != item.Name)&lt;/pre&gt;

  &lt;pre class="alt"&gt;    {&lt;/pre&gt;

  &lt;pre&gt;        &lt;span class="rem"&gt;// Get the part of the expression tree that represents this parameter&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;        var mexp = call.Arguments[i];&lt;/pre&gt;

  &lt;pre&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;        DbType type = GetDbType(item);&lt;/pre&gt;

  &lt;pre&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;object&lt;/span&gt; val = String.Empty;&lt;/pre&gt;

  &lt;pre&gt;        &lt;/pre&gt;

  &lt;pre class="alt"&gt;        Parameter param = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre&gt;        Type attribute = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;

  &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;string&lt;/span&gt; propertyName = String.Empty;&lt;/pre&gt;

  &lt;pre&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (mexp &lt;span class="kwrd"&gt;is&lt;/span&gt; ConstantExpression)&lt;/pre&gt;

  &lt;pre&gt;        {&lt;/pre&gt;

  &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;// If it's a constant expression (for example if the parameter is 0 or a string &amp;quot;test&amp;quot;)&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;            ConstantExpression c = mexp &lt;span class="kwrd"&gt;as&lt;/span&gt; ConstantExpression;&lt;/pre&gt;

  &lt;pre class="alt"&gt;            val = c.Value; &lt;span class="rem"&gt;// just use this value&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;        }&lt;/pre&gt;

  &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="kwrd"&gt;if&lt;/span&gt; (mexp &lt;span class="kwrd"&gt;is&lt;/span&gt; MethodCallExpression)&lt;/pre&gt;

  &lt;pre&gt;        {&lt;/pre&gt;

  &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;// The following if is a special case if we want our parameter to be a session parameter&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;        &lt;span class="rem"&gt;// We could add more special cases for ControlParameter etc.&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;            var m = mexp &lt;span class="kwrd"&gt;as&lt;/span&gt; MethodCallExpression;&lt;/pre&gt;

  &lt;pre&gt;            &lt;span class="rem"&gt;// Check to see if the method call has the Session attribute applied to it&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (m.Method.GetCustomAttributes(&lt;span class="kwrd"&gt;false&lt;/span&gt;).Count&amp;lt;&lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt;(y =&amp;gt; y.GetType() == &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(SessionAttribute)) &amp;gt; 0)&lt;/pre&gt;

  &lt;pre&gt;            {&lt;/pre&gt;

  &lt;pre class="alt"&gt;                attribute = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(SessionAttribute);&lt;/pre&gt;

  &lt;pre&gt;                &lt;span class="rem"&gt;// Just get the session field name to construct the SessionParameter&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;                val = Execute&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;(m.Arguments[0]);&lt;/pre&gt;

  &lt;pre&gt;            }&lt;/pre&gt;

  &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="kwrd"&gt;if&lt;/span&gt; (m.Method.GetCustomAttributes(&lt;span class="kwrd"&gt;false&lt;/span&gt;).Count&amp;lt;&lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt;(y =&amp;gt; y.GetType() == &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ControlAttribute)) &amp;gt; 0)&lt;/pre&gt;

  &lt;pre&gt;            {&lt;/pre&gt;

  &lt;pre class="alt"&gt;                attribute = &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ControlAttribute);&lt;/pre&gt;

  &lt;pre&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;                &lt;span class="rem"&gt;// A method call to pass a ControlParameter looks like this:&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;                &lt;span class="rem"&gt;// ODSHelper.Control&amp;lt;string, string&amp;gt;(() =&amp;gt; txtName.Text)&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;                &lt;span class="rem"&gt;// Argument[0] contains the lambda expression&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;                &lt;span class="rem"&gt;// We just have to get the object name and the property name&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;                &lt;span class="rem"&gt;// and we have the values required for our ControlParameter&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;                &lt;span class="rem"&gt;// Argument[0] contains the lambda () =&amp;gt; txtName.Text&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;                &lt;span class="rem"&gt;// This is an UnaryExpression and we have to get the right&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;                &lt;span class="rem"&gt;// side of this expression which is txtName.Text and analyze it&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;                var unary = ((UnaryExpression)m.Arguments[0]).Operand;&lt;/pre&gt;

  &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;                val = LambdaHelper.GetObjectID(unary);&lt;/pre&gt;

  &lt;pre class="alt"&gt;                propertyName = LambdaHelper.GetProperty(unary);&lt;/pre&gt;

  &lt;pre&gt;            }&lt;/pre&gt;

  &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;            {&lt;/pre&gt;

  &lt;pre class="alt"&gt;                &lt;span class="rem"&gt;// Here the expression is more complex, maybe we are calling something like a method or accessing a property&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;                &lt;span class="rem"&gt;// to get our value back.  To get the value of the parameter passed, we actually need to construct&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;                &lt;span class="rem"&gt;// a new lambda, compile it and execute it to get the value returned and passed to our method call.&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;                val = Execute&amp;lt;&lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt;(mexp);&lt;/pre&gt;

  &lt;pre class="alt"&gt;            }&lt;/pre&gt;

  &lt;pre&gt;        }&lt;/pre&gt;

  &lt;pre class="alt"&gt;        &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;            val = Execute&amp;lt;&lt;span class="kwrd"&gt;object&lt;/span&gt;&amp;gt;(mexp);&lt;/pre&gt;

  &lt;pre class="alt"&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (val != &lt;span class="kwrd"&gt;null&lt;/span&gt;)&lt;/pre&gt;

  &lt;pre class="alt"&gt;        {&lt;/pre&gt;

  &lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (attribute == &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(SessionAttribute))&lt;/pre&gt;

  &lt;pre class="alt"&gt;                param = &lt;span class="kwrd"&gt;new&lt;/span&gt; SessionParameter(item.Name, type, val.ToString()); &lt;span class="rem"&gt;// val contains the session field name&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="kwrd"&gt;if&lt;/span&gt; (attribute == &lt;span class="kwrd"&gt;typeof&lt;/span&gt;(ControlAttribute))&lt;/pre&gt;

  &lt;pre class="alt"&gt;                param = &lt;span class="kwrd"&gt;new&lt;/span&gt; ControlParameter(item.Name, type, val.ToString(), propertyName); &lt;span class="rem"&gt;// val contains the control name&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;                param = &lt;span class="kwrd"&gt;new&lt;/span&gt; Parameter(item.Name, type, val.ToString()); &lt;span class="rem"&gt;// val contains the value passed to the method&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;            &lt;span class="rem"&gt;// Here we make sure that the parameter is not already added in the collection.&lt;/span&gt;&lt;/pre&gt;

  &lt;pre&gt;            &lt;span class="rem"&gt;// If it's there but it doesn't have the same value, we have to remove and add it&lt;/span&gt;&lt;/pre&gt;

  &lt;pre class="alt"&gt;            &lt;span class="kwrd"&gt;bool&lt;/span&gt; contain = ds.SelectParameters.Contains(item.Name);&lt;/pre&gt;

  &lt;pre&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (!contain)&lt;/pre&gt;

  &lt;pre class="alt"&gt;                ds.SelectParameters.Add(param);&lt;/pre&gt;

  &lt;pre&gt;            &lt;span class="kwrd"&gt;else&lt;/span&gt; &lt;span class="kwrd"&gt;if&lt;/span&gt; (contain &amp;amp;&amp;amp; !ds.SelectParameters.HasValue(item.Name, val.ToString()))&lt;/pre&gt;

  &lt;pre class="alt"&gt;            {&lt;/pre&gt;

  &lt;pre&gt;                ds.SelectParameters.Remove(item.Name);&lt;/pre&gt;

  &lt;pre class="alt"&gt;                ds.SelectParameters.Add(param);&lt;/pre&gt;

  &lt;pre&gt;            }&lt;/pre&gt;

  &lt;pre class="alt"&gt;        }&lt;/pre&gt;

  &lt;pre&gt;    }&lt;/pre&gt;

  &lt;pre class="alt"&gt;    i++;&lt;/pre&gt;

  &lt;pre&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
  &lt;br /&gt;I think the comments explain the code pretty well.&amp;#160; I suggest that you download the complete source code to see the code for other method calls. &lt;/p&gt;

&lt;h2&gt;SessionParameter and ControlParameter&lt;/h2&gt;

&lt;p&gt;I want to get back to the syntax I chose to specify a parameter shall be a SessionParameter, a ControlParameter or simply a plain old Parameter.&amp;#160; We are using two static methods from ODSHelper class: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;[Session]&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; T Session&amp;lt;T&amp;gt;(&lt;span class="kwrd"&gt;string&lt;/span&gt; sessionFieldName)&lt;/pre&gt;

  &lt;pre class="alt"&gt;{&lt;/pre&gt;

  &lt;pre&gt;    &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;default&lt;/span&gt;(T);&lt;/pre&gt;

  &lt;pre class="alt"&gt;}&lt;/pre&gt;

  &lt;pre&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;[Control]&lt;/pre&gt;

  &lt;pre&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;static&lt;/span&gt; T Control&amp;lt;T&amp;gt;(Expression&amp;lt;Func&amp;lt;object&amp;gt;&amp;gt; action)&lt;/pre&gt;

  &lt;pre class="alt"&gt;{&lt;/pre&gt;

  &lt;pre&gt;    &lt;span class="kwrd"&gt;return&lt;/span&gt; &lt;span class="kwrd"&gt;default&lt;/span&gt;(T);&lt;/pre&gt;

  &lt;pre class="alt"&gt;}&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&amp;#160; &lt;br /&gt;Basically those methods do nothing.&amp;#160; They only return a default value of type T.&amp;#160; Why are we doing that?&amp;#160; We need to satisfy the parameter type of the method call where they will be used. &lt;/p&gt;

&lt;p&gt;Why is it important to return T and not simply return a string?&amp;#160; For example, take the following method to be used as an ODS SelectMethod: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; TestCollection FetchAll(DateTime date, &lt;span class="kwrd"&gt;string&lt;/span&gt; search, &lt;span class="kwrd"&gt;string&lt;/span&gt; user, &lt;span class="kwrd"&gt;string&lt;/span&gt; sort, &lt;span class="kwrd"&gt;int&lt;/span&gt; startRowIndex, &lt;span class="kwrd"&gt;int&lt;/span&gt; maximumRows)&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&amp;#160; &lt;br /&gt;You want to pass this method with SetSelectMethod&amp;lt;T&amp;gt;.&amp;#160; The main difference is the first parameter.&amp;#160; It’ll take its value from the Session. Session[“FieldName”] will return a DateTime.&amp;#160; Here is how we could do it: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;ObjectDataSource src = MainDataSource &lt;span class="kwrd"&gt;as&lt;/span&gt; ObjectDataSource;&lt;/pre&gt;

  &lt;pre&gt;&amp;#160;&lt;/pre&gt;

  &lt;pre class="alt"&gt;src.SetSelectMethod&amp;lt;ItemGroupController&amp;gt;(&lt;/pre&gt;

  &lt;pre&gt;ctrl =&amp;gt; ctrl.FetchAll(&lt;/pre&gt;

  &lt;pre class="alt"&gt;ODSHelper.Session&amp;lt;DateTime&amp;gt;(&amp;quot;FieldName&amp;quot;),&lt;/pre&gt;

  &lt;pre&gt;ODSHelper.Session&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;(SharedSessionVariables.GetSearchField(Page)),&lt;/pre&gt;

  &lt;pre class="alt"&gt;HttpContext.Current.User.Identity.Name, &lt;/pre&gt;

  &lt;pre&gt;String.Empty, 0, 0))&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&amp;#160; &lt;br /&gt;Pay attention to the first parameter.&amp;#160; What is important is that our method compiles and that our expression analyzer has enough information to fill the SessionParameter properties.&amp;#160; When we analyze the expression tree, we’ll be able to look at the parameter passed to the method call Session and see the Session field name. &lt;/p&gt;

&lt;p&gt;The second really important characteristic is the custom attributes that both methods have ([Session] and [Control]).&amp;#160; Those unique attributes are used when we walk the expression tree to know what kind of parameter we need to create.&amp;#160; Without them, we could not distinguish between normal method calls and those that are here to give us information in the expression tree. &lt;/p&gt;

&lt;h3&gt;SessionParameter&lt;/h3&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;ODSHelper.Session&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;(SharedSessionVariables.CurrentProductAuthority)&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&amp;#160; &lt;/p&gt;

&lt;p&gt;or &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;ODSHelper.Session&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;(&lt;span class="str"&gt;&amp;quot;CurrentProductAuthority&amp;quot;&lt;/span&gt;)&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&amp;#160; &lt;br /&gt;If you don’t have a Session wrapper that defines the constant session field names, you can use the second option, which is more direct. When we analyze the expression tree, we simply evaluate the expression inside and create a SessionParameter with the result of the evaluation as the session field name. &lt;/p&gt;

&lt;h3&gt;ControlParameter&lt;/h3&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;ODSHelper.Control&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;(() =&amp;gt; txtName.Text)&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&amp;#160; &lt;br /&gt;This is how we pass in a control parameter.&amp;#160; Once again, we have a direct reference on the control and its property. If we remove the control later, our app won’t compile. txtName is this context is simply a TextBox that we have in our page. &lt;/p&gt;

&lt;p&gt;ControlParameter needs two main things, the name of the control and the name of the property of the control.&amp;#160; We can get those two things by analyzing the expression tree of the passed lambda expression and add our ControlParameter in the parameter collection accordingly. &lt;/p&gt;

&lt;h3&gt;Parameter&lt;/h3&gt;

&lt;p&gt;For parameter inside our lambda expression that we pass, for example: &lt;/p&gt;

&lt;div class="csharpcode"&gt;
  &lt;pre class="alt"&gt;HttpContext.Current.User.Identity.Name&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;&amp;#160; &lt;br /&gt;The expression analyzer simply executes this code and sets the returned value as the default value of the parameter. &lt;/p&gt;

&lt;h3&gt;Other Parameter Types&lt;/h3&gt;

&lt;p&gt;Right now my code is only supports those three kinds of parameters, but it would be pretty easy to add the other types of parameters (Query, Cookie, etc) with a little bit more work. Furthermore, the same logic could apply to the Update/Insert/Delete methods used by the ODS. &lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;We need your opinion!&amp;#160; I think this project could be pretty useful.&amp;#160; The syntax is not very elegant, but I can't see a better way of doing this at this point.&amp;#160; Please download the project, play with the code, and give us your feedback! Ideally, this is something that would be built into the framework at a later date, but in the meantime we can still play around with it! This might not be the only way to solve the issues we are tackling, and if you have any other suggestions, let us know! (Example: Extending the ODS for each controller?) &lt;/p&gt;

&lt;p&gt;&lt;a href="http://blog.lavablast.com/ODSTyped/Example1-ShowAll.aspx" target="_blank"&gt;Example Page with Northwind&lt;/a&gt; | &lt;a href="http://blog.lavablast.com/ODSTyped/LavaBlast.ODS.Web.zip" target="_blank"&gt;Download Source&lt;/a&gt;&lt;/p&gt;
&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2008%2f12%2fObjectDataSource-Typed-Select-method-with-Lambda-Expressions.aspx"&gt;&lt;img alt="kick it on DotNetKicks.com" src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2008%2f12%2fObjectDataSource-Typed-Select-method-with-Lambda-Expressions.aspx&amp;amp;border=660000&amp;amp;fgcolor=660000&amp;amp;bgcolor=FF9900&amp;amp;cbgcolor=FFFF00" border="0" /&gt;&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/WP8lDg5BsSI" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/WP8lDg5BsSI/post.aspx</link>
      <author>EtienneT</author>
      <comments>http://blog.lavablast.com/post/2008/12/02/ObjectDataSource-Typed-Select-method-with-Lambda-Expressions.aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=6f632558-afd5-4ca3-a7e2-dc0eead457e5</guid>
      <pubDate>Tue, 02 Dec 2008 17:42:00 -0500</pubDate>
      <dc:publisher>EtienneT</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=6f632558-afd5-4ca3-a7e2-dc0eead457e5</pingback:target>
      <slash:comments>2</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=6f632558-afd5-4ca3-a7e2-dc0eead457e5</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2008/12/02/ObjectDataSource-Typed-Select-method-with-Lambda-Expressions.aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=6f632558-afd5-4ca3-a7e2-dc0eead457e5</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=6f632558-afd5-4ca3-a7e2-dc0eead457e5</feedburner:origLink></item>
    <item>
      <title>Software usability is like eggnog</title>
      <description>&lt;p&gt;
Yesterday, I had the chance to participate in a formal usability study at the &lt;a href="http://www.uottawa.ca"&gt;University of Ottawa&lt;/a&gt;. &lt;a href="http://thevirtualmachine.wordpress.com/"&gt;Marconi Lanna&lt;/a&gt;&amp;rsquo;s master&amp;rsquo;s project is an improved code difference visualization tool. I had already played with the tool in the past and found it interesting but only saw its true value during the formal usability test where I was given specific tasks to perform. I will keep the details about this tool for a later post when it becomes publicly available, but participating in the study helped me realize a few things: 
&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;&lt;strong&gt;Creating a proper usability study is hard.&lt;/strong&gt; 
	&lt;ul&gt;
		&lt;li&gt;There are so many ways you can introduce bias in a test, it isn&amp;#39;t funny. For this particular test, I feel that it was very close to being unbiased and most of my initial concerns were addressed. &lt;/li&gt;      
		&lt;li&gt;One concern that does remain is the fact that the test cases were crafted by the researcher and could have been manipulated to generate a specific set of results. However, crafted test cases are required for any kind of comparison between the users. I personally feel the test was unbiased as it presented potential flaws in both the default tool and the proposed tool. &lt;/li&gt;      
		&lt;li&gt;This particular test was for a tool used by software developers. As you probably know, &lt;a href="http://www.joelonsoftware.com/articles/HighNotes.html"&gt;some developers demonstrate productivity levels an order of magnitude higher than others&lt;/a&gt;.&amp;nbsp; You can imagine that this makes it hard to analyze productivity results collected during a study. &lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;        
	&lt;li&gt;&lt;strong&gt;Don&amp;#39;t forget to make the test subjects communicate&lt;/strong&gt;
	&lt;ul&gt;
		&lt;li&gt;If a subject does a task and says nothing, you don&amp;#39;t learn 10% of what you could have learned about their thought process. In usability studies, the thought process is the most important thing to understand. &lt;/li&gt;      
		&lt;li&gt;Everyone is different, and having them express their concerns/procedures helps you analyze the results. &lt;/li&gt;      
		&lt;li&gt;This is not a discussion. Because of the desire for unbiased results during the questions, you cannot ask specific questions (which may lead to bias) or comment on the answers (which will definitely lead to bias). However, after everything is done and recorded, having discussions with the subject can enhance your overall understanding of what they said &lt;a href="http://usability.jameshom.com/thnkalod.htm"&gt;while thinking aloud&lt;/a&gt;. &lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;        
	&lt;li&gt;&lt;strong&gt;Working on usability is a self-improvement task that you must work on every day. &lt;/strong&gt;
	&lt;ul&gt;
		&lt;li&gt;It&amp;#39;s easy to continuously postpone enhancements to the the non-functional aspects (performance, security, reliability, usability, etc.) of a software system when you&amp;#39;re a small startup. &lt;/li&gt;      
		&lt;li&gt;You should log even seemingly superficial non-functional concerns in your bug database just as with any other issue. Customers that complain are rare and you must take their comments seriously. &lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;     
&lt;/ol&gt;
&lt;p&gt;
Following my own advice, I want to look at one of the usability issues that I filed in our bug system a few months ago, our bug #1377. I like revealing some internal details of the issues we face and appreciate your input on our strategies. Note: if you&amp;#39;re not interested in a real-world example at a complex usability issue, skip to the conclusion of this post as this could be a snooze-fest!
&lt;/p&gt;
&lt;h1&gt;A closer look at one of our usability issues&lt;/h1&gt;  
&lt;p&gt;
&lt;a href="http://www.flickr.com/photos/jpdaigle/3050529389/"&gt;&lt;img style="border: 0px none ; margin: 10px" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/UsabilityStudies_A8FD/eggnog_3.jpg" border="0" alt="Drink too much eggnog and you too will have usability issues" width="356" height="239" align="right" /&gt;&lt;/a&gt; Usability is one of our core values when building software. However, we sometimes slip up, as with anything! Looking back at the comments received over the last year by our users, I can clearly identify the single most important usability problem we have with our wide range of software solutions for the franchise industry. The issue is related to our &lt;a href="http://lavablast.com/en/lb_screencast_eventbookings.aspx"&gt;party/event/appointment booking module&lt;/a&gt; which is a part of our &lt;a href="http://lavablast.com/en/lb_pointofsale.aspx"&gt;franchise point of sale (POS) software&lt;/a&gt;. I&amp;#39;d like to drill down into this feature and present the usability issue and how it came to surface. 
&lt;/p&gt;
&lt;h3&gt;Background information&lt;/h3&gt;  
&lt;p&gt;
A &lt;a href="http://lavablast.com/en/lb_pointofsale.aspx"&gt;POS system&lt;/a&gt; basically lets you sell items in a retail store. Our is integrated with a frequent buyer program, and we register information concerning each member in the database. Depending on the franchise brand, different information is tracked. (We can do cool things when customers are willing to provide information such as their postal or email address, but as a general rule, all fields are optional). We also have a party booking module which allows parents to book their child&amp;#39;s birthday party at a store. The feature accomplishes two things: it acts as a calendar system (to avoid booking overlaps) and is integrated with the sale module to support deposits. Obviously, when someone books an event at a store and pays a deposit, the system needs to remember who paid what, so that when the big day comes, the deposit can be applied to the transaction. 
&lt;/p&gt;
&lt;p&gt;
To make a long story short, we offer a number of configuration options in the background to support different scenarios used by different franchises. Here are some examples:
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Book a child birthday party in one of multiple rooms.&amp;nbsp; You reserve the room for a period of time for your exclusive use. 
	&lt;ul&gt;
		&lt;li&gt;This is akin to booking a meeting room. &lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;        
	&lt;li&gt;Book a space in one of multiple play areas. You reserve your spot in the play room, but the room is shared up to its capacity. 
	&lt;ul&gt;
		&lt;li&gt;This is akin to booking your table in a &lt;a href="http://www.thecodefactory.ca"&gt;co-working location&lt;/a&gt;. &lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;        
	&lt;li&gt;Book a particular employee for your haircut or spa services. 
	&lt;ul&gt;
		&lt;li&gt;Rooms are not the only resource that can be booked. This ties in to the employee management system. &lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;        
	&lt;li&gt;Book a table, a pitcher of beer, and that hot new employee to serve at your table. &lt;/li&gt;    
	&lt;li&gt;You&amp;#39;re booking a particular resource and pay a:&amp;nbsp; 
	&lt;ul&gt;
		&lt;li&gt;Flat fee (party package)&lt;/li&gt;      
		&lt;li&gt;Flat fee per guest&lt;/li&gt;      
		&lt;li&gt;The fee will depend on what is bought at a later date. &lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;        
	&lt;li&gt;Stores have the possibility to ask for a deposit, and if they do, it can be of a particular % of the transaction or a fixed dollar amount. 
	&lt;ul&gt;
		&lt;li&gt;The store decides if deposits are refundable. (in the event of a cancellation). &lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;     
&lt;/ul&gt;
&lt;p&gt;
As you can see, there are numerous elements at play here that make it non-trivial in our context, where we design the booking system to meet each franchise&amp;#39;s particular booking needs. What is important at a high-level, however, is we defined the following use cases when researching the requirements for this module. 
&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;(Deposit is required) A member is in the store and wants to book an event. &lt;/li&gt;    
	&lt;li&gt;&lt;strong&gt;(Deposit is required) A member is on the phone, wants to book and event and will come by later this week to pay for the deposit. &lt;/strong&gt;&lt;/li&gt;    
	&lt;li&gt;(Deposit is not required) A member wants to create a booking. &lt;/li&gt;    
	&lt;li&gt;Employees want to peruse the list of bookings for a particular date range. &lt;/li&gt;    
	&lt;li&gt;Employees want to add an additional deposit amount to a transaction (regardless of the fact that deposits are required or not)&lt;/li&gt;    
	&lt;li&gt;Employees want to finalize an event, and apply the total deposit amounts to the transaction. &lt;/li&gt;    
	&lt;li&gt;An employee books a &amp;quot;fairy princess&amp;quot; birthday party for another employee, without their knowledge or consent, and invites their friends and family. The birthday boy frantically tries to cancel the event as soon as possible, to avoid long-term trauma. &lt;/li&gt;    
	&lt;li&gt;(and a few more, less important scenarios). &lt;/li&gt; 
&lt;/ol&gt;
&lt;h3&gt;We chose to divide our scenarios contextually &lt;/h3&gt;  
&lt;p&gt;
We decided that the best way to solve this problem was to create two ways to access the booking information, one which is contextually associated to a store member and one for higher-level tasks. 
&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;From the existing sales page in the POS (for all scenarios that work with money)
	&lt;ul&gt;
		&lt;li&gt;The cashier first selects a member from the frequent member program (or create a new account)&lt;/li&gt;      
		&lt;li&gt;The system displays a new button called bookings &lt;/li&gt;      
		&lt;li&gt;The cashier selects bookings and progresses through the following menus (create a new booking, additional deposit, cancellation, finalize event). &lt;/li&gt;      
		&lt;li&gt;The system then returns back to the sale page, where the cashier can process the sale/refund. &lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;        
	&lt;li&gt;From the events page in the POS (for all scenarios that don&amp;#39;t deal with money)
	&lt;ul&gt;
		&lt;li&gt;The system displays an Outlook-like calendar of all the events, allowing employees to peruse the events&lt;/li&gt;      
		&lt;li&gt;Employees can view availabilities, change rooms, change employees, change date&amp;amp;time, enter additional event information from this page. &lt;/li&gt;      
		&lt;li&gt;&lt;strong&gt;Employees can create an event without a deposit, which the system presents as being in the &amp;quot;Deposit Required&amp;quot; state. &lt;/strong&gt;
		&lt;ul&gt;
			&lt;li&gt;This is to support scenario #2 listed above. You&amp;#39;d create a tentative booking, which you&amp;#39;d be willing to double book if the person never comes by to pay for the deposit. &lt;/li&gt;        
			&lt;li&gt;Also, this is used by franchises that don&amp;#39;t require deposits instead of going to the sale page, and becomes similar to a paper calendar in a hair salon. &lt;/li&gt;     
		&lt;/ul&gt;
		&lt;/li&gt;         
	&lt;/ul&gt;
	&lt;/li&gt;     
&lt;/ol&gt;
&lt;h3&gt;The usability problem&lt;/h3&gt;  
&lt;p&gt;
After being in use for about 18 months, I can state that we need to step back and re-evaluate this feature, to improve its usability. The system works fine when used as intended, but I have observed the following issues:
&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;If deposits are not required, untrained cashiers book events in the event page and never finalize them.
	&lt;ul&gt;
		&lt;li&gt;They do open up the sale page, possibly select the member, and sell whatever was bought without associating the sale with the booking. &lt;/li&gt;      
		&lt;li&gt;This is bad because we&amp;#39;re left with open events and can&amp;#39;t track cancellation rates, sales per member, etc.&amp;nbsp; (bad for the store) &lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;        
	&lt;li&gt;If deposits are required, untrained cashiers assume they need to go to the events module to do anything related to events. 
	&lt;ul&gt;
		&lt;li&gt;They book an event, but the system marks it as &amp;quot;Deposit Required&amp;quot; and doesn&amp;#39;t let them change the deposit amount. &lt;/li&gt;      
		&lt;li&gt;This is bad as they are under-using the system, not following store policies, and sending in support requests to have the system explained to them. (bad for the store and bad for LavaBlast)&lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;     
&lt;/ul&gt;
&lt;p&gt;
As a side note, I am honestly always surprised when a cashier expects to book an event, accept money but not track any information to associate the deposit with the person making the payment. That&amp;#39;s like their store manager expecting them to come in to work and not bothering to write their name on their timesheet. 
&lt;/p&gt;
&lt;h3&gt;Root cause&lt;/h3&gt;  
&lt;ul&gt;
	&lt;li&gt;We&amp;#39;ve divided the scenarios in two groups: the ones that deal with money and the ones that do not. &lt;/li&gt;    
	&lt;li&gt;Combine this with the following facts: 
	&lt;ul&gt;
		&lt;li&gt;Some stores don&amp;#39;t use deposits. Therefore, money is only involved when finalizing the event at which point the main task is selling products/services, not dealing with events. &lt;/li&gt;      
		&lt;li&gt;Even if we use deposits, we allow the creation of temporary bookings (in the deposit required state). &lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;     
&lt;/ul&gt;
&lt;p&gt;
&lt;strong&gt;To recap, to improve usability, we&amp;#39;ve defined processes that contextually lead the cashier to performing their tasks. However, since the core starting point (deposit amounts) varies completely from one franchise store to another, our contextual division doesn&amp;#39;t make sense.&lt;/strong&gt; 
&lt;/p&gt;
&lt;h3&gt;Possible Solutions&lt;/h3&gt;  
&lt;ul&gt;
	&lt;li&gt;&lt;a href="http://www.flickr.com/photos/jpdaigle/3050529389/"&gt;&lt;img style="border: 0px none ; margin: 10px" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/UsabilityStudies_A8FD/eggnog2_3.jpg" border="0" alt="Possible solutions" width="360" height="241" align="right" /&gt;&lt;/a&gt;Solution #1: In the store events page, after selecting an event, give the option to the cashier to add a deposit, cancel or finalize the event. This would bring the user from the events page to the sale page and allow them to follow our defined processes. 
	&lt;ul&gt;
		&lt;li&gt;Possible issue: We&amp;#39;re making them switch contexts implicitly. When they&amp;#39;re done, they would wonder why there were in the sale module instead of the events module. (do we force them back to the events module when they are done?)&lt;/li&gt;      
		&lt;li&gt;Possible issue: they want to finalize an event that has already occurred (to get rid of &amp;quot;open&amp;quot; events, caused by the described usability issue). &lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;        
	&lt;li&gt;Solution #2: In the sale page, make the booking button available even when no member has been selected. Once clicked, prompt to select a member or offer to peruse the list of bookings. Selecting an event would auto-select the member, and bring us to the usual process. 
	&lt;ul&gt;
		&lt;li&gt;Can be combined with the previous solution. &lt;/li&gt;      
		&lt;li&gt;Possible issue: This allows cashiers to do anything at any time while in the sale module and loses the contextual value of the process we&amp;#39;ve defined. One main problem in the POS industry is the constant desire to put all options on the page, flooding the user with too many options and making the POS hard to use and increases training costs. &lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;        
	&lt;li&gt;Solution #3: Do the previous solutions and get rid of the top level events page. 
	&lt;ul&gt;
		&lt;li&gt;If everything can be done from the sale module (and in most cases the sale module is where the cashier spends most of their day), why bother having a separate mode, that adds confusion. &lt;/li&gt;      
		&lt;li&gt;Possible issue: cashiers won&amp;#39;t instinctively feel that the sale page is where they want to go for event bookings, they might not even discover that events exist! &lt;/li&gt;   
	&lt;/ul&gt;
	&lt;/li&gt;        
	&lt;li&gt;Solution #4: Cut the feature set to attack the root causes directly
	&lt;ul&gt;
		&lt;li&gt;If deposits are required, get rid of the feature that allows us to create temporary bookings. 
		&lt;ul&gt;
			&lt;li&gt;Possible issue: people will complain that they can&amp;#39;t do this anymore. As deposits are required, this is a case of tough love. &lt;/li&gt;     
		&lt;/ul&gt;
		&lt;/li&gt;            
		&lt;li&gt;If deposits are never required, don&amp;#39;t integrate with the sale module at all. 
		&lt;ul&gt;
			&lt;li&gt;Possible issue: decrease the perceived value of the event module and lose a few potential reports (which are not in use at this point). &lt;/li&gt;     
		&lt;/ul&gt;
		&lt;/li&gt;         
	&lt;/ul&gt;
	&lt;/li&gt;     
&lt;/ul&gt;
&lt;p&gt;
At this point, we&amp;#39;ll probably end up implementing #1, #2, and #4. One of our core technological themes is usability, therefore we&amp;#39;re willing to live with cutting a few features for an improved process. However, the issue is still up for discussion and I&amp;#39;d love to hear your opinions on this specific matter or on similar problems you&amp;#39;ve experience in your software startups. 
&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;  
&lt;p&gt;
We&amp;#39;ve built a flexible core to support the varied needs of franchisors. Everything works, but are we offering too many features to our customers? Should we cut back to make it simpler on everyone? In our case, I think so. If I had to sum up the usability problem discussed today into a high level lesson learned, I&amp;#39;d state the following: Don&amp;#39;t separate use case scenarios into different groups if a configuration option invalidates the grouping premise. 
&lt;/p&gt;
&lt;p&gt;
During this holiday season, I think I&amp;#39;ll take some time to re-read a few of my favorite books on usability: 
&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/0321344758?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=0321344758"&gt;Steve Krug - Don&amp;#39;t Make Me Think&lt;/a&gt;&lt;/li&gt;    
	&lt;li&gt;&lt;a href="http://www.amazon.com/gp/product/1893115941?ie=UTF8&amp;amp;tag=shadeca-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;creativeASIN=1893115941"&gt;Joel Spolsky - User Interface Design for Programmers&lt;/a&gt;&lt;/li&gt; 
&lt;/ol&gt;
&lt;p&gt;
Although these are great reads, they deal with the low-level user interface design problems, not with higher-level software engineering &lt;a href="http://en.wikipedia.org/wiki/Feature_interaction_problem"&gt;feature interaction problems&lt;/a&gt; in software product lines. I&amp;#39;d be curious to know if there any good resources on this subject (other than overly academic conference papers/workshops). I&amp;#39;d love to hear more first-hand, real-world experiences from other bloggers. 
&lt;/p&gt;
&lt;h3&gt;Easy to use software is like eggnog. &lt;/h3&gt;  
&lt;ul&gt;
	&lt;li&gt;Reaching the desired results takes time. (&lt;a href="http://www.chow.com/recipes/10758"&gt;The best eggnog takes at least three weeks to make&lt;/a&gt;). &lt;/li&gt;    
	&lt;li&gt;Users will get a kick out of it. (With &lt;a href="http://www.chow.com/recipes/10758"&gt;as much bourbon as milk&lt;/a&gt;, eggnog will definitely get people talking.)&lt;/li&gt;    
	&lt;li&gt;At a high level, anyone can claim to be a pro. However, the devil is in the details. &lt;/li&gt;    
	&lt;li&gt;You need to listen to your users to make the recipe better in the future. &lt;/li&gt;    
	&lt;li&gt;What would the holiday season be without it? (okay, that doesn&amp;#39;t make sense!)&lt;/li&gt; 
&lt;/ul&gt;
&lt;a href="http://www.dotnetkicks.com/kick/?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2008%2f11%2fSoftware-usability-is-like-eggnog.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fblog.lavablast.com%2fpost%2f2008%2f11%2fSoftware-usability-is-like-eggnog.aspx&amp;amp;border=660000&amp;amp;fgcolor=660000&amp;amp;bgcolor=FF9900&amp;amp;cbgcolor=FFFF00" border="0" alt="kick it on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/KHYmHjJNjmM" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/KHYmHjJNjmM/post.aspx</link>
      <author>JKealey</author>
      <comments>http://blog.lavablast.com/post/2008/11/27/Software-usability-is-like-eggnog.aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=c607b350-6f2f-4def-8acf-6559b026f100</guid>
      <pubDate>Thu, 27 Nov 2008 01:15:00 -0500</pubDate>
      <category>Ideas</category>
      <category>Software</category>
      <dc:publisher>JKealey</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=c607b350-6f2f-4def-8acf-6559b026f100</pingback:target>
      <slash:comments>5</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=c607b350-6f2f-4def-8acf-6559b026f100</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2008/11/27/Software-usability-is-like-eggnog.aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=c607b350-6f2f-4def-8acf-6559b026f100</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=c607b350-6f2f-4def-8acf-6559b026f100</feedburner:origLink></item>
    <item>
      <title>LavaBlast Software named Startup of the Day by Microsoft</title>
      <description>&lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.microsoftstartupzone.com/BizSparkDB/Pages/Featured_Company.aspx"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; margin: 10px; border-right-width: 0px" height="54" alt="FLY-002_StartupZone_Badge-CompanyOfTheDay_IR5a" src="http://blog.lavablast.com/image.axd?picture=WindowsLiveWriter/LavaBlastSoftwarenamedStartupoftheDaybyM_93E0/FLY-002_StartupZone_Badge-CompanyOfTheDay_IR5a_3.jpg" width="140" align="right" border="0" /&gt;&lt;/a&gt; We recently joined the &lt;a href="http://www.microsoft.com/bizspark/"&gt;Microsoft BizSpark&lt;/a&gt; program after seeing posts about it on a few blogs (&lt;a href="http://www.flowventures.com/blog/index.php/2008/11/05/free-ms-software-for-startups/"&gt;Flow Ventures&lt;/a&gt; and &lt;a href="http://www.thecodefactory.ca/blog/2008/11/05/bizspark-&amp;ndash;-start-up-support/"&gt;The Code Factory&lt;/a&gt;). To make a long story short, amongst the hundreds of startups that register each day from around the world, we've been selected as the most promising startup of the day. It is always fun to get recognition for our hard work! &lt;/p&gt;  &lt;p&gt;In any case, you can view the &lt;a href="http://www.microsoftstartupzone.com/BizSparkDB/Pages/Featured_Company.aspx"&gt;interview here&lt;/a&gt; (today only!). &lt;/p&gt;  &lt;p&gt;Have a nice weekend! &lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/LavablastSoftwareBlog/~4/3MFjvFcxylk" height="1" width="1"/&gt;</description>
      <link>http://feedproxy.google.com/~r/LavablastSoftwareBlog/~3/3MFjvFcxylk/post.aspx</link>
      <author>jkealey</author>
      <comments>http://blog.lavablast.com/post/2008/11/15/LavaBlast-Software-named-Startup-of-the-Day-by-Microsoft.aspx#comment</comments>
      <guid isPermaLink="false">http://blog.lavablast.com/post.aspx?id=3779809a-8fae-4528-838b-6dd06f99c552</guid>
      <pubDate>Sat, 15 Nov 2008 10:37:37 -0500</pubDate>
      <category>News</category>
      <dc:publisher>jkealey</dc:publisher>
      <pingback:server>http://blog.lavablast.com/pingback.axd</pingback:server>
      <pingback:target>http://blog.lavablast.com/post.aspx?id=3779809a-8fae-4528-838b-6dd06f99c552</pingback:target>
      <slash:comments>2</slash:comments>
      <trackback:ping>http://blog.lavablast.com/trackback.axd?id=3779809a-8fae-4528-838b-6dd06f99c552</trackback:ping>
      <wfw:comment>http://blog.lavablast.com/post/2008/11/15/LavaBlast-Software-named-Startup-of-the-Day-by-Microsoft.aspx#comment</wfw:comment>
      <wfw:commentRss>http://blog.lavablast.com/syndication.axd?post=3779809a-8fae-4528-838b-6dd06f99c552</wfw:commentRss>
    <feedburner:origLink>http://blog.lavablast.com/post.aspx?id=3779809a-8fae-4528-838b-6dd06f99c552</feedburner:origLink></item>
  </channel>
</rss>
