Archive for May, 2008

When technology isn't the power behind a killer software feature

Tuesday, May 6th, 2008

Like so many people I waited until the last minute to do my 2007 taxes. After some deliberation I decided to prepare them myself with the caveat that I’d need software to help.  I did a little research and decided to go with Intuit’s TurboTax desktop version.  Intuit also offers an online version of Turbotax for about half the price but I wanted control over my data and didn’t want to get locked into one vendor for next year.

On whole I think they’ve gotten the user experience down to about a B+.  Its questionnaire format does a good job of guiding you through the various tax issues and making sure you’re saving as much money as is allowed.  Once completed it can be a little confusing when you need to change something but overall my complaints were minor. Solid but unremarkable software.

Except… Turbotax had a trump up its sleeve.  It turns out they’ve negotiated data sharing agreements with a range of payroll providers, including mine. I was shocked to discover that in about 60 seconds (after entering my provider’s ID and then a figure from my W2 as a “password”) all of my wage information was imported automatically.  No typing, no double checking.  Wow! What a killer feature. Not only did it validate my decision to purchase Turbotax it got me excited enough to come into the office the next day and rave about it.

It struck me that the real work in including this feature wasn’t accomplished at the keyboard but in the board room.  Sure it was probably hard work to format and import the data but the real sweat was in getting all those payroll providers to sign up and share their information. It seems like an obvious move but it took someone at Intuit to not only have the bright idea but to pick up the phone and start calling payroll companies.

I think software folks too often attack problems with a strictly technical mindset.  Can you picture the W2 brainstorming meeting? “What if we have them scan the W2 then OCR the data? Wait! What if they enter only half the fields and we can calculate the rest?” Technical wizardry is great but it’s only one tool in the software palette. It’s easy for developers (and ex-developer manager-types) to default to this way of thinking. It’s in their comfort zone, it doesn’t require unpleasantness like negotiations and contracts, and it offers the chance to write whiz-bang code. For some challenges the technical approach works, but for others it’s sub-optimal or just impossible.

The marketplace has lots of examples of software that wouldn’t have succeeded without this approach: Apple’s iTunes store, OpenTable’s reservations, Seamlessweb’s ordering.  All of these work because people at those companies were willing to step out of the realm of code and do some old-fashioned horse trading. In the context of software as a product, the non-technical approach can mean golden opportunities for companies versatile enough to take it. 

Java Quick Tip: Restlet & Spring Integration

Monday, May 5th, 2008

Here at arc90 we use the Restlet framework for Java REST web services. Recently, we’ve also been considering Spring for dependency injection and AOP within these services. Restlet 1.0.x provides an extension for Spring integration, but I had a hard time finding an example of its use, so I thought I would post one here.

DemoApplication Class

package com.arc90.demo;
import ...
/**
* Starts the Restlet application and defines its resources
*/
public class DemoApplication extends Application
{
private static final String RESOURCE_MAP = "resourceMap";
private static final int HTTP_PORT = 8182;
public DemoApplication(Context context)
{
super(context);
}
/**
* Initialize the application when this class is executed.
* @param args
*/
public static void main(String[] args)
{
try
{
Component component = new Component();
component.getServers().add(Protocol.HTTP, HTTP_PORT);
DemoApplication application = new DemoApplication(component.getContext());
component.getDefaultHost().attach(application);
component.start();
}
catch (Exception e)
{
/*
* Convert any checked exceptions to unchecked for the purpose of this demo.
*/
throw new RuntimeException(e);
}
}
/**
* Set up the spring-defined resources
*/
public Restlet createRoot()
{
Router router = new Router(getContext());
/*
* Retrieve a map of available resources from spring and attach each to the router
*/
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] { "applicationContext.xml" });
Map<String, Finder> resourceMap = (Map<String, Finder>) context.getBean(RESOURCE_MAP);
for (String key : resourceMap.keySet())
{
router.attach(key, resourceMap.get(key));
}
return router;
}
}

DemoResource Class

package com.arc90.demo;
import ...
/**
* Handles all requests to the demo resource.
*/
public class DemoResource extends Resource
{
@Override
public void init(Context context, Request request, Response response)
{
super.init(context, request, response);
getVariants().add(new Variant(MediaType.TEXT_PLAIN));
}
@Override
public Representation getRepresentation(Variant variant)
{
return new StringRepresentation("Restlet Spring Integration Demo", MediaType.TEXT_PLAIN);
}
}

Spring applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
<util:map id="resourceMap">
<entry key="/demo">
<bean id="configurationFinder" class="org.restlet.ext.spring.SpringFinder">
<lookup-method name="createResource" bean="demoResource"/>
</bean>
</entry>
</util:map>
<!-- Resources classes -->
<bean name="demoResource"
class="com.arc90.demo.DemoResource" scope="prototype" />
</beans>

This uses Restlet’s built in HTTP server, so to access the demo resource, simply run DemoApplication, then point your browser to: http://localhost:8182/demo

As you can see, it takes a little non-Spring code to get Restlet up and running. However, the Resources themselves are Spring beans, so everything from the Resource level down can be wired by Spring.

Restlet version 1.1 (currently in testing) provides improved Spring integration where the entire Restlet framework can be configured in the Spring application context. I plan to post another quick tip demonstrating the 1.1 configuration soon.

JetBlue has a great writing staff

Sunday, May 4th, 2008

JetBlue Logo

I signed up for JetBlue’s TrueBlue point program today, and I was greeted with this success page:

Thanks for joining. Your TrueBlue number is ###. Unless you’ve got a photographic memory, you might want to print this page for your reference. (You can also cut out the card below for your traveling convenience – or to impress your friends.)

Awesome. Made me smile and I immediately felt good about booking a flight with them (I hadn’t yet, as I wanted to get the number before I booked). They really sealed the deal.

In addition, they had a really concise, clear way to describe the terms of the program:

TrueBlue points live for 1 year. So to earn your free flight, you need to accumulate your 100 points in a consecutive 12 month period. Just to be clear, that means any unused points automatically expire on their first birthday. For example, if you earn 12 points on 12/31/04, those 12 points expire on 12/31/05.

No complicated rules, just a plan English description in a down-to-earth tone.

We’re currently working on our first product offering at Arc90, and crafting the writing within and about a product isn’t easy. It’s good to see that JetBlue took the time to put some effort and creativity into how they speak to their customers.

Supporting Milliseconds Date Formatting in Strtotime()

Thursday, May 1st, 2008

Today I ran into an issue where the values being returned from datetime columns in my MSSQL database were unreadable by PHP’s strtotime() function.

Here’s what was happening: All the values were in this format: Aug 27 2007 12:00:00:000AM. This was problematic because PHP’s strtotime() function cannot parse dates where milliseconds are separated from the seconds by a colon rather than a period (dot).

At first, I was concerned about why this was happening and, according to a bug report on php.net, it was due to the combination of FreeTDS + php5.2.3 + MSSQL and their formatting of columns cast as datetime. Although it’s fixed in the trunk of PHP’s repository, it hasn’t made its way to a release. So, if you happen to run into this–here’s a solution:

strtotime(preg_replace("/:\d{3}([AP]M)$/", " $1", $timeStamp));

In this case, $timeStamp is equal to Aug 27 2007 12:00:00:000AM. So use preg_replace() to replace the milliseconds (:000) with a space and then puts the am/pm back at the end. Now strtotime() has a value that’s readable.

That’s it!