Lazy thread-safe collections in Java

I recently needed to update some Java code that was frequently making unecessary calls to an external web service. Depending on the particular UI component, or the item being displayed in that component, a large set of the data being retrieved ended up not being displayed. My task was to update the code so that it only made external calls to the web service when the data was going to be displayed.

My first thought was to pass along a flag from the view layer that would indicate whether or not to retrieve the data in question. This would require updating all of the UI classes that displayed the data, as well as passing the new flag through several additional method calls to prevent the data from being collected when it wasn’t needed.

I did not care for this solution because it meant changing several classes so that details of the view could be passed down into the data retrieval logic. Instead, I came up with an alternative solution, the crux of which is a generic, thread-safe, delayed evaluation Map.

Instead of the business layer service returning a fully populated Map of data, it returns a Map that will contain that data, if it is accessed. If no user interface components request the data for display, the data will never be retrieved. This meant that I did not have to change any code outside of wrapping the logic for populating the Map with a new LazyImmutableMap. Here is an example of how it is used:

1
2
3
4
5
6
7
8
9
10
11
public Map<String, String> getData(final Integer id) {
  return new LazyImmutableMap<String, String>(
    
    new Callable<Map<String, String>() {

      public Map<String, String> call() throws Exception {      
        return externalService.getSomeData(id);
      }
    
  });
}

The call to externalService.getSomeData() will only be invoked if something tries to access an element in the LazyImmutableMap. Implementing the class itself turned out to be extremely simple with the help of Google Collections’ ForwardingMap (see this previous post for more on Google Collections) and the standard Java concurrency class FutureTask.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class LazyImmutableMap<K,V> extends ForwardingMap<K,V> {
  public static class AccessException extends RuntimeException {
    public AccessException(Throwable cause) {
      super(cause);
    }
  }

  private final FutureTask<Map<K, V>> task;

  public LazyImmutableMap(final Callable<Map<K,V>> eval) {    
    task = new FutureTask<Map<K,V>>(new Callable<Map<K,V>>() {
      public Map<K, V> call() throws Exception {
        return ImmutableMap.copyOf(eval.call());
      }
    });
  }

  @Override
  protected Map<K, V> delegate() {
    task.run();
    try {
      return task.get();
    } catch (InterruptedException e) {
      throw new AccessException(e);
    } catch (ExecutionException e) {
      throw new AccessException(e.getCause());
    }    
  }
}

The constructor creates a FutureTask that when accessed will return an immutable copy of the Map provided by the passed in Callable.

Extending ForwardingMap means that I only needed to implement the delegate method to get a fully compliant Map class. The FutureTask takes care of preventing the passed in Callable from being executed more than once, and it’s thread-safe, so any number of threads can access an instance of the map simultaneously.

Unfortunately Java’s checked exceptions add quite a bit of clutter to an otherwise very clean and simple class. The AccessException is needed because the normal Map interface does not allow for checked exceptions to be thrown. The get() method on a FutureTask can throw two different types of exceptions. They need to be handled differently, so they are both caught and properly wrapped in an AccessException (a subclass of RuntimeException) and re-thrown.

Using this technique I was able to retrieve the external data if and only if it was going to be displayed to the user. And I was able to do it without breaking the separation between the view and the business logic.

Filed in: Languages, Tips

Environment Configurable

During our last big rails project, Bloomfire, we found ourselves integrating with all kinds of external services. Because of this we had a diverse set of environment dependent configuration variables. A consistent pattern started to arise where we would extract our configuration variables into a YAML file and then wrap the configuration using a small class wrapper. This eventually gave rise to Environment Configurable, a library that makes environment dependent configuration easy in rails.

Read the rest of this entry
Filed in: Technologies, Tools

Engineer Thinking, Making People Feel Like Idiots & The Failure of Empathy

Matt Legend Gemmell on Engineer Thinking:

All too often, when faced with a decision about how to implement certain functionality, engineers take the extreme position that:

  1. A feature must be exactly what 100% of users want.
  2. If the above isn’t true (and it almost never is), the feature must be configurable.

This binary approach is gravely wrong, and unjustly offloads decision-making onto the user of the software. We’ve all seen where this approach ends up: multi-row sets of tabs, scrolling panes of checkboxes, nested radio-buttons and a general overload of configuration.


Matt Linderman (37signals) saying Computers shouldn’t make people feel like idiots:

For those of us surrounded by the minutiae of computers all day, it’s easy to forget there’s a world of people out there who just don’t get it. And it’s not their fault. It’s ours.


Mike Monteiro (Mule Design Studio) on The Failure of Empathy:

As an industry, we need to understand that not wanting root access doesn’t make you stupid. It simply means you do not want root access. Failing to comprehend this is not only a failure of empathy, but a failure of service.


(via ignore the code)

Don't forget the basics: opening keynote of the Michigan Agile and Beyond conference

The Michigan Agile and Beyond Conference brought 450 people together this weekend in Dearborn, Michigan to teach, learn and debate the broad subject of agile software development. The “beyond” part of the theme bothered me a little, for the same reason that it irritates me when I hear companies explaining that they do “practical agile”. Both statements miss the point that agile is about nothing more than delivering valuable software frequently and regularly. What can be more practical than that? Why do we need to move “beyond” that?

Ron Jeffries & Chet Hendrickson offered the best advice I heard all day in their opening keynote. Namely, to go beyond, you must travel through. Their point was that if you’re not in the “agile space” now, successfully using the core practices, then get there before you start tinkering or adapting. It reminded me of the debate in the early 2000s of whether you had to use all (then) 13 practices of XP to claim you were doing XP. Someone (might even have been Ron) responded that you’re doing XP when you’ve mastered all 13 practices and figured out which ones you need and how to use them on your project.

Chet cut straight to the chase when he said it matters what you do, because “agile isn’t any damn thing.” Ron followed that by quoting Emerson Codd: “The truth isn’t like a bunch of puppies running around where you chose your favorite.” Their talk was as casual, funny, and inspiring as always. They shared some of the simple truths about software development:

  • Software development is all about people (Funny Ron quote: “When I got into computing, it was not with the intention of hanging out with other people.”) Tools can’t replace or compensate for people.
  • Requirements need to get into the heads of the people building the software. Documents are a poor way of doing this.
  • If you can’t ship working software every month, try two weeks. If you can’t do it in two weeks, try one week. You learn to build software by building and shipping working software.
  • Waterfall doesn’t give you accurate information until it’s too late.
  • If your software isn’t bug free, you can’t trust your velocity.
  • If you’re manually testing everything your need for testers grows at least linearly with the number of iterations. You’ve got to automate some of those tests.
  • If you can’t refactor, you can’t do evolutionary design.

They summarized by identifying the three non-negotiable core practices to being agile:

  • Ship software regularly
  • Test as you go
  • Refactor to evolve the design

While their message, at least to me, was fundamentally one of “don’t forget the basics”, Chet and Ron also addressed the “beyond” part of the conference. They cited relationship to management, human resources, and dealing with projects that don’t fit in a single room as challenges that are still beyond the scope of accepted agile thinking. Ron called out Pillar Technology and Atomic Object as examples of companies pushing the boundaries of agile. They also reminded us that we have an obligation to share our experiments, both our failures and successes, to grow the body of agile knowledge. Chet added that these experiments push agile into new territory, but are still grounded in the simple statements of the Agile Manifesto.

Responsible Estimation Tool

Many times during the sales process we are asked to give a cost estimation based on our early understanding of an application’s core features. In order to responsibly and efficiently estimate, we conduct a 50/90 range analysis. We use this technique because we want to establish a responsible middle ground between our optimistic and pessimistic estimates. Our estimate is appropriately buffered for the risk we see in the defined tasks.

This spreadsheet is the tool that we use to conduct our analysis. Tasks can be estimated in any time duration that you choose (i.e. hours, days, weeks). At the start of a large project we generally estimate tasks in days or weeks.

Startup Weekend in Grand Rapids

I attended the tail end of Startup Weekend at The Factory this weekend. Kudos to Michael Boyink and Aaron Schaap for organizing this in West Michigan. Marc Nager, one of two people who run Startup Weekends all over the country, commented on the energy in the room and the incredible willingness of strangers to get behind ideas and work together. Evidently what we lack in VC we make up for in hard work and generosity.

The final pitches were pretty good considering that some of these ideas weren’t even hatched until Friday afternoon. An app for helping people keep track of things that need to get done even had a pretty functional backend implemented over the weekend. It was great to see eight teams and 40 or so people challenging the economic doom and gloom of our state, putting their ideas out there, listening to others, and taking action to make them real.

git-deploy to three servers in 83 characters

I smiled this morning when I typed this command to git-deploy fresh code to three machines in quick succession.

Sure, there’s nothing groundbreaking here that you can’t do with plenty of other tools. But it was still fun.

Atomic Cookies

No, they won’t explode.

Resident cookie-master, Marissa Christy, went the extra mile yesterday for the SoftwareGR meeting. While our meetings always have great snacks, Marissa really raised the bar this time with Atomic Object logo cookies:

Atomic Object logo cookies

If you want some great snacks, and great information too, come to the next SoftwareGR meeting.

"All input is evil until proven otherwise. That's rule number one."

Yesterday SoftwareGR hosted Dr. Richard Enbody from MSU. He spoke about security, and he quoted the above security rule from the book Writing Secure Code by Michael Howard and David LeBlanc (sample chapter.)

The main topic of the talk was recent research performed by Richard’s team to develop a new security feature called Secure Bit.

Read the rest of this entry

Better Java with Google Collections

Version 1.0 of the Google Collections library was officially released December 30, 2009. I have been using the library for the past 6 months or so on a variety of Java projects, with great success. Today I gave a brown-bag talk to share with some interested Atoms.

I have included below the sample code I used as presentation material, in case others are interested.

Read the rest of this entry