rc3.org

Strong opinions, weakly held

Month: March 2010 (page 3 of 4)

It’s a Maven world

Dave Johnson on why he’s migrating his build scripts for Roller from Ant to Maven:

I was a Maven hater and resisted it for a long time but over the years Maven has gotten much better, it’s well supported in IDEs and as far as I can tell, Maven has replaced Ant as the de facto build system for Java projects. If you want new developers be able to easily build, debug and run your code via command or their favorite IDE then Maven is the way to go, and that’s especially true for open source projects like Roller.

This is my impression as well. I started to create a new, open source Java application the other day, and it occurred to me that for the build script, I should use Maven because it seems like that’s the way everybody is handling dependency management these days. Dave’s post is one more indication that I was probably right about that.

The future of Tivo

The good news is that Tivo is releasing a new DVR. It looks awesome. I have been a Tivo customer for many years, in fact, Tivo is perhaps my favorite product of all time.

The bad news is that Tivo is a perpetual money loser, and lost 730,000 subscribers last year.

For many years I’ve had a DVR provided by Time-Warner hooked up to the second TV. It is the worst product I use on a regular basis. The interface is terrible, the remote is ungainly, and the performance of the device is pathetic. But what we’ve learned over time is that people are not willing to pay the premium for the Tivo experience — the cable company DVRs are good enough for most people. I doubt that many people even know that the Tivo is so much better.

One big problem Tivo faces is that it’s a lot easier for a cable company to ship a DVR that you just plug in and start using than it is for Tivo to do the same. In addition to the Tivo, we have two cable cards that enable it to tune in digital channels, and a tuning adapter that the Tivo needs to tune in SDV channels. Getting the CableCards to work was incredibly painful and required several visits from the cable guy and many phone calls with tech support, and the tuning adapter required for SDV crashes frequently and sometimes takes the Tivo down with it. The miserable piece of crap that is the cable company DVR just works.

I’ve always wished that a cable company would license Tivo’s software and use it in their own DVRs, but it has never happened. That’s a pity, because I continue to worry about Tivo’s viability as an independent concern.

Time versus priorities

Here’s Scott Berkun on The Cult of Busy:

The phrase “I don’t have time for” should never be said. We all get the same amount of time every day. If you can’t do something it’s not about the quantity of time. It’s really about how important the task is to you.

When I was in college around 20 years ago, I remember a friend telling me that he tried to read the Wall Street Journal every day. I told him I didn’t have time to read the newspaper, which was a ridiculous statement, because I always had plenty of time to play Tetris in the dorm or go out and eat cheap Tex-Mex. He told me that it wasn’t a question of time, but of priorities. I don’t know that I ever claimed not to do something because I didn’t have time again. This was one of the few really useful lessons that I really picked up early.

Jeopardy and Family Feud

Fred Clark argues that on health care reform, Democrats are playing Jeopardy while Republicans are playing Family Feud:

At the recent health care reform “summit,” Republican leaders made it clear that they’re not interested in playing Jeopardy. That would be a losing proposition against President Ken Jennings. Obama was eager to show that he really does have the right answers — cost containment, near-universal coverage, lower premiums, better quality care, deficit reduction. All of that is well covered in the plan he’s pushing and any attempt to challenge him on the facts would be doomed.

So the GOP has decided to play a different game — to switch from Jeopardy to Family Feud. That way it’s not about the facts, or about what works, or about the actual effect of actual policies on actual people. In the subjective guessing-game of Family Feud, none of that matters. Family Feud is all about perceptions — about what those hundred people surveyed think or guess or dimly remember having heard something about.

This is perhaps my favorite blog post I’ve read this year and a gold medal winner in the Metaphor Olympics.

Running the numbers on Toyota safety

Robert Wright breaks down the numbers on Toyota’s safety record given all of the recent reports of uncontrolled acceleration:

My back-of-the-envelope calculations (explained in a footnote below) suggest that if you drive one of the Toyotas recalled for acceleration problems and don’t bother to comply with the recall, your chances of being involved in a fatal accident over the next two years because of the unfixed problem are a bit worse than one in a million — 2.8 in a million, to be more exact. Meanwhile, your chances of being killed in a car accident during the next two years just by virtue of being an American are one in 5,244.

This is the article I’ve been looking for since the mass hysteria about acceleration problems began. It strikes me as undoubtable that a Toyota purchased today is significantly safer than most of the cars I’ve driven over the course of my life. I used to own a 1977 Ford pickup truck that caught on fire under the hood more than once.

Update: It’s also worth mentioning that if the accelerator sticks on your cars, there are several ways to stop.

Building a new service from scratch, followup

About a month ago I asked what people would use to build a new Web service from scratch, without providing any real details on what the application would do. People seemed to all agree that basing the service on JSON was the best way to go, and that’s the route I took, so I thought I’d write it up.

In this case, I have a PHP front end talking to a Java back end that uses Spring and Hibernate. The application in question is the administrative console for a test harness for a SOAP Web service. We use a third party SOAP service and needed the ability to provide responses that are guaranteed to always be the same for the purpose of creating tests. So my test harness stands in for the third party service, requiring only a URL change, and returns the data managed via the client application to which my question referred.

The application itself is very simple — it’s a single table with a lookup mechanism. The client enables users to find and edit individual records from the table. I needed four basic operations:

  • Search for records
  • Retrieve a single record
  • Update a record
  • Create a record
  • Delete a record

The Server

This is a Java application that uses Spring and Spring MVC. I use Spring’s Web Services support for the SOAP stuff, but it wasn’t worth it to bother with that for the administrative client. So instead I mapped every URL ending in .json to a Spring DispatcherServlet. Like all sensible Java developers who’ve worked in Rails, I hate configuration in general and XML configuration files in particular. So I used Spring’s ControllerClassNameHandlerMapping, which extracts the path from the URL and maps it to a Spring controller.

So a path like /myapp/responses/search.json calls the search() method in the ResponsesController class. Simple.

I won’t go into the boring details of my implementation, other than to talk about how I’m passing the messages back and forth. The client uses standard form submission (name and value pairs) to submit the data, and expects to get JSON back. So in the case of search, I extract the field name and query value, go to the database and run a query to retrieve the records in question, and then package them up and encode them using JSON to be sent back to the client.

The JSON encoding is handled by Google’s GSON library, and it’s ridiculously simple. It accepts a plain Java object, and serializes it and all of its children using reflection. Any properties that are null are omitted. So for my application, I created a wrapper object that was well-suited to being encoded using GSON. It includes a hash for error messages, a property for a single response object, and a collection of response objects for search results.

Then I created a view for Spring called SimpleGsonView, which accepts a wrapper (or any object), encodes it using GSON, and then sends the results to the client. Here’s the whole thing:

@Override
protected void renderMergedOutputModel(Map aMap, 
    HttpServletRequest aRequest, 
    HttpServletResponse aResponse) throws Exception
{
    Object wrapper = aMap.get("wrapper");
    Gson gson = new Gson();
    aResponse.setContentType("application/json");
    ServletOutputStream out = aResponse.getOutputStream();
    out.print(gson.toJson(wrapper).replaceAll("\\\\n", ""));
    logger.debug(gson.toJson(wrapper));
    out.flush();
}

One thing you may notice is that I do a search and replace on the JSON. It removes all occurrences of \n from the encoded data. That’s a hack to get around a client side bug that I got tired of trying to fix. I’ll talk about that later.

So for this application, to add new functionality, I just add a new method to my controller, process the input parameters and put the correct results in the wrapper, and then pass that to the view I created. Very simple. I added an autocomplete feature to the search page, and this is all I had to do to add support for it on the back end:

public ModelAndView complete(HttpServletRequest aRequest, 
    HttpServletResponse aResponse)
{
    String searchField = aRequest.getParameter("field");
    String searchQuery = aRequest.getParameter("query");

    List<String> results = Collections.emptyList();

    if (searchField.equals(FIRST_NAME))
    {
        results = getFirstNameCompletions(searchQuery);
    }
    else if (searchField.equals(LAST_NAME))
    {
        results = getLastNameCompletions(searchQuery);
    }
    else if (searchField.equals(KEY))
    {
        results = getKeyCompletions(searchQuery);
    }
    else if (searchField.equals(ZIP))
    {
        results = getZipCompletions(searchQuery);
    }

    return new ModelAndView(new SimpleGsonView(), "wrapper", 
        results);
}

The methods to retrieve the data are in another class, but I removed the reference to it for simplicity.

So that’s the server piece. I could have done more to make it RESTful but since the service is just for me, I went with the simplest possible approach.

The Client

For legacy reasons the client is a PHP application, and I decided to use jQuery and a little PHP to implement the client features. Here’s how each operation is implemented:

Search: The search feature is pure jQuery. The home page for the client has a search form on it with a pulldown of fields that can be searched and a text box for the search terms. I used the autocomplete feature from jQuery UI to make the search form a bit friendlier. I catch the submission of the search from (or the selection of an item in the list provided by autocomplete) and submit the results to the server using jQuery’s getJSON() method. Then I create a table on the page with the search results in it.

Retrieve a single record: This is the only place I used PHP to connect to the server. I decided that I wanted each item to have its own URL, and although I could have written the code to pull the key for the item in the URL, fetch it, and build the page in JavaScript, I opted to use PHP instead. To do so, I grabbed a copy of the Services_JSON library and included that in my app. PHP supports JSON natively starting with version 5.2, but my server is running PHP 5.1. I chose Services_JSON because it’s a single file. So when you go to the edit or show page, I use PHP to fetch the JSON for the key in the URL from the server and put it on the detail page or in the edit form. This is where the bug comes in. My service returns XML documents (encoded as a value in a JSON hash), and sometimes Services_JSON won’t decode the data properly. When I started stripping \n from the XML, it worked, so I went with it.

Create a record: This form uses jQuery’s post() method to submit the contents of the form to the server. If the request returns a successful result, it redirects the user to the detail page for the new item. If it doesn’t, it displays the errors included in the result.

Update a record: This form works just like the create form.

Delete a record: This is the simplest operation of all. It submits the key to be deleted, and the server simply returns true or false.

Upshot

This was just about the easiest implementation of an application and accompanying Web service I’ve ever done, in spite of the fact that I’m a pretty terrible JavaScript developer. The server side was much easier to implement than I’d anticipated, it took less code in Java than it would have in PHP, and not much more code than it would have taken in Ruby. This is the application that really convinced me that there’s essentially no development overhead involved with using AJAX rather than traditional server-side request processing when building Web applications, at least if you bring libraries like jQuery and GSON to the party to make things easier.

Anil Dash on the mashup culture

So what happens when vast numbers of social networking citizens find another law that they consider irrelevant?

Anil Dash on the political implications of millions of people uploading derivative works with no regard for copyright law.

An adult discussion of ads

ArsTechnica explains why they ran an experiment Friday that hid their content for people running ad blocking software. It’s a very grown up and pragmatic explanation, and it’s almost too obvious to even quote. The point I found interesting was the discussion of the advertising death spiral — when advertising impressions go down, sites have to take on ads that pay more per impression — those are the ads that take over the whole page, hide the links you want to click on, and so forth. Those ads are awful for users and many publications are running them on the front page. If I ran an advertising-funded site, and I could increase (or maintain) revenue by either shutting down ad blockers or presenting more obnoxious ads to the people who aren’t running ad blockers, the decision would be a no-brainer. I’d prefer to inconvenience the people who think they should get my content for free every time.

The wisdom of Wal-Mart

Meeting social and environmental standards is not optional. I firmly believe that a company that cheats on overtime and on the age of its labor, that dumps its scraps and its chemicals in our rivers, that does not pay its taxes or honor its contracts will ultimately cheat on the quality of its products. And cheating on the quality of products is the same as cheating on customers. We will not tolerate that at Wal-Mart.

Wal-Mart CEO Lee Scott at a conference for suppliers in China. I’m not the world’s biggest fan of Wal-Mart, but I can’t argue with Scott’s logic or principles in this case.

Universal design

In yesterday’s post about URL literacy a little debate broke out in the comments about whether it’s worth it to add usability features for novices if they make life more difficult for experts. On that note, last month’s Dwell had an article on universal design, a school which argues that designers should be trying to create designs that work for everyone, regardless of their level of experience or capabilities. This is the position I was arguing in the comments, and is an approach I’m fond of. I generally reject the notion that experts and novices require wildly different interfaces or devices, although obviously there are outliers in any group whose tastes may differ.

Older posts Newer posts

© 2024 rc3.org

Theme by Anders NorenUp ↑