Archive

Archive for the ‘open source’ Category

Glazed Lists – an essential Java library for lists and tables

May 2, 2012 9 comments

Swing is the built in toolkit for creating user interfaces for Java programs. While these types of standalone desktop applications are becoming less prevalent, perhaps due to increasing functionality of webapps, there are still some industries which are highly reliant on them. If you find yourself creating a Java desktop application, you will probably have to learn Swing, and you will also probably have to learn to display information to the user in list or table form. In standard Java Swing applications, it is difficult, or at least annoying, to do the following three tasks:

  1. Displaying domain specific models
  2. Filtering
  3. Sorting

Glazed Lists is an open source project that makes all three of these tasks trivial. Its primary author, Jesse Wilson, is a current Google employee. Let’s examine each of these aspects in turn.

Provides a simplified API for representing objects within a JTable

Swing uses the Model View Controller paradigm throughout. Thus the table or list merely presents a view for an underlying model data structure. Part of your job in displaying data in a Swing table is to define the TableModel implementation which provides the data for the JTable to display.

Swing provides an AbstractTableModel that does most of the work for you, requiring you only to implement the following methods:

public int getRowCount();
public int getColumnCount();
public Object getValueAt(int row, int column);

Here’s a simple domain model object we might want to visualize in a table:

public class Person {
    int age;
    String name;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge() { return age; }

    public String getName() { return name; }
}

The logical way of doing that would be two have two columns, one for the age, one for the name. Let’s make a table model for this case:

public class PersonTableModel extends AbstractTableModel {
    private static final String[] columns = {"Name", "Age"};
    private final List people;

    public PersonTableModel(List people) {
        // Make a defensive copy
        this.people = new ArrayList(people);
    }

    public int getRowCount() {
        return people.size();
    }
    public int getColumnCount() {
        return columns.length;
    }
    public Object getValueAt(int row, int column) {
        Person p = people.get(row);
        if (column == 0) {
            return p.getName();
        } else {
            return p.getAge();
        }
    }
}

This certainly works, but it requires a fair bit of boilerplate. Furthermore, the code above does not provide any way of modifying the list of people after it is copied by the TableModel.

Glazed Lists simplifies your life by treating the table not as an arbitrary two dimensional grid, but instead as a collection of rows, where the rows are kept in sync with changes to the domain models that they represent. All you have to do is define how a row is laid out, and Glazed Lists takes care of the rest.

The interface you need to use in order to define how the table looks and which aspects of your model objects are exposed is called [TableFormat][].

The interface is as follows:

  • int getColumnCount() – The number of columns to display.
  • String getColumnName(int column) – Gets the title of the specified column.
  • Object getColumnValue(E baseObject, int column) – Gets the value of the specified field for the specified object.

This should remind you of the TableModel interface presented previously, but note how the getColumnValue method is different – rather than getting a row and column, and forcing you to look up the object corresponding to that row, you are provided the object directly.

Here is a TableFormat which allows Person objects to be easily visible in a JTable:

public class PersonTableFormat implements TableFormat {

    String[] columnNames = {"Name", "Age"};
    private static final int NAME_INDEX = 0;
    private static final int AGE_INDEX = 1;

    public int getColumnCount() { return columnNames.length; }

    public String getColumnName(int column) { return columnNames[i]; }

    public Object getColumnValue(Person baseObject, int column) {
        switch (column) {
            case NAME_INDEX:
                return baseObject.getName();
            case AGE_INDEX:
                return baseObject.getAge();
            default:
                throw new IllegalArgumentException("Expected column 0 or 1, got " + column);
        }
    }
}

While this isn’t too hard to write, it’s still a lot of boilerplate (and not significantly different from the previous example). Glazed Lists makes it even easier than this. The entire class definition above can be replaced with three lines:

TableFormat personTableFormat = GlazedLists.tableFormat(Person.class,
    // Names of the properties to fetch
    new String[] {"name","age"},
    // Names for the columns
    new String[] {"Name", "Age"});

What’s this doing? And how can it do all that I had previously in one line of code? Well, it requires and takes advantage of JavaBeans naming convention. The static function uses reflection to find the methods mapping to properties named “name” and “age”. In this case, it looks for two methods, getName() and getAge(), both of which it finds. (If I didn’t name my methods appropriately, I would get a runtime exception). The second array defines the strings that should be used to identify the corresponding entry in the properties array. In other words, element 0 in the names column is used to identify the property name at index 0.

This TableFormat class alone is insufficient to display data in a table. To do that, you need a class which fulfills the TableModel interface I described previously. Fortunately, Glazed Lists makes this easy.

The fundamental building block of Glazed Lists is the EventList class. It is similar to the ArrayList class in Java, except that it has support for observers. If you’re not familiar with the Observer/Observable design pattern, it allows objects (observers) to register themselves and receive notifications whenever a different object (the observable) is changed. For instance, when a new item is added to the EventList, the UI element representing it on screen automatically refreshes itself.

The EventTableModel class fulfills the TableModel interface, making use of the EventList and TableFormat we described earlier. The EventList is the data provider, and the TableFormat determines how to extract the data from the EventList and display it in the table.

EventList people = new BasicEventList();
// Add all the elements
for (Person p : getPeople()) {
    personList.add(p);
}
TableFormat personTableFormat = GlazedLists.tableFormat(Person.class,
    // Names of the properties to fetch
    new String[] {"name","age"},
    // Names for the columns
    new String[] {"Name", "Age"});
EventTableModel tableModel = new EventTableModel(people, personTableFormat);
JTable table = new JTable(tableModel);
// Any modifications to the ‘people’ list is automatically reflected in the table

Provides a simplified means of filtering a table or list

Perhaps one of the most important features of any interactive table is the ability to filter out extraneous information. Glazed Lists makes this possible by chaining together EventList transformations; these transformations provide a different view of the underlying data. When the original model is modified, the filtered views automatically pick up the changes and update accordingly.

Say we want to provide the ability to filter the list based on people’s names. We will add a listener to a text field which listens for changes (new letters typed or deleted), and filters the list in real time. Once we have an EventList of some sort, it is easy to create a new “view” of that same list, filtering out entries you don’t want to see. You do this by wrapping the list in a FilterList, and then assigning some sort of filter criterion. Let’s start simple with a filtered list which only shows those users whose names start with the letter ‘A’.

EventList personList = new BasicEventList();
personList.add(new Person("Anthony Hopkins", 74));
personList.add(new Person("Barack Obama", 50));
personList.add(new Person("American McGee", 39));

Matcher personFilter = new Matcher() {
    public boolean matches(Person p) {
        return p.getName().startsWith("A");
    }
};
// Create a filtered list
FilterList filteredList = new FilterList(personList, personFilter);
// Displaying the people in a list as opposed to a table; could also create EventTableModel
// as in the last example.
EventListModel filteredListModel = new EventListModel(personList)
JList list = new JList(filteredListModel);
// At this point, shows Anthony Hopkins and American McGee

The filter I’ve defined above is static – once it’s instantiated, its filter condition never changes. Glazed Lists supports dynamic filters as well, through the MatcherEditor interface. We will see how to use a MatcherEditor instance for a text field, but first we need to tell Glazed Lists which strings to use when filtering for a given object. We do this with the TextFilterator interface.

Picture illustrating a FilterList which accepts only those people whose name starts with 'A'

 

public class PersonTextFilterator imlements TextFilterator {
    // Slightly strange interface, but done for efficiency reasons
    public getFilterStrings(List baseList, Person element) {
        baseList.add(element.getName());
        // Allow users to filter by age as well
        baseList.add(String.valueOf(element.getAge()));
    }
}

The MatcherEditor class to use in our case is TextComponentMatcherEditor. We provide it with the text field that it will use as the filter source, as well as an instance of the PersonTextFilterator class we just defined.

EventList personList = new BasicEventList();
personList.add(new Person("Anthony Hopkins", 74));
personList.add(new Person("Barack Obama", 50));
personList.add(new Person("American McGee", 39));

JTextField filterTextField = new JTextField();
// Add the text field to the UI - add to a JPanel

// Hook the text field up to a filter list
MatcherEditor filter = new TextComponentMatcherEditor(filterTextField, new PersonTextFilterator());

// Create a filtered list
FilterList filteredList = new FilterList(personList, filter);
EventListModel filteredListModel = new EventListModel(filteredList)
JList list = new JList(filteredListModel);
// List automatically updates in response to typing in the text field

Each transformed EventList is itself an EventList, meaning it can also be used as the basis of an EventListModel or EventTableModel. This chaining capability is extremely powerful.

Provides sorting capabilities

Finally, Glazed Lists makes it extremely easy to implement rich sorting capabilities in your tables or lists.

As we saw in the last example, it is possible to wrap a given EventList to provide a different view. In this case, we will have a sorted view of the data, which automatically updates whenever the underlying data changes.

To create the SortedList, you need to make your domain object implement Comparable, or create a Comparator. For instance,

public class PersonNameComparator implements Comparator {
    public int compare(Person p1, Person p2) {
        return p1.getName().compareTo(p2.getName());
    }
}
EventList personList = new BasicEventList();
personList.add(new Person("Anthony Hopkins", 74));
personList.add(new Person("Barack Obama", 50));
personList.add(new Person("American McGee", 39));

Comparator nameComparator = new PersonNameComparator();
// Create a sorted list decorator
SortedList sortedList = new SortedList(personList, nameComparator);
EventListModel sortedListModel = new EventListModel(sortedList)
JList list = new JList(filteredListModel);

A SortedList, wrapping a standard EventList

While the above example works for JLists, it’s nice to be able to sort a JTable as well. This is not too hard, either, as long as you have set up a TableFormat instance as described in the first section of this post. In essence, the TableFormat defines the type of each column, which is then used to sort the table whenever the corresponding column header is clicked. This behavior is defined in the TableComparatorChooser class, which exposes a static method to perform the installation on the target JTable. Here’s an example:

Comparator nameComparator = new PersonNameComparator();
// Create a sorted list decorator
SortedList sortedList = new SortedList(personList, nameComparator);
EventTableModel peopleTableModel = new EventTableModel(sortedList, new PersonTableFormat());
JTable peopleTable = new JTable(peopleTableModel);

// Use MULTIPLE_COLUMN_MOUSE to allow sorting by multiple columns, or SINGLE_COLUMN
// to sort by just a single column
TableComparatorChooser tableSorter = TableComparatorChooser.install(
    peopleTable, sortedList, TableComparatorChooser.MULTIPLE_COLUMN_MOUSE);

// At this point, clicking on the table headers will sort by this column

As the more detailed Glazed Lists tutorial warns,

By default, TableComparatorChooser sorts by casting column values to Comparable. If your column’s values are not Comparable, you’ll have to manually remove the default Comparator using TableComparatorChooser.getComparatorsForColumn(column).clear().

As long as your columns are represented by Comparable classes such as Number or String, you shouldn’t have to worry about this caveat.

Conclusion

Glazed Lists is one of the best Java Swing libraries I’ve ever used. It simplifies life for the programmer as well as the end user of the software project, since tables that allow sorting and filtering are far more useful than those which do not. If you do any sort of Swing programming, you owe it to yourself to try this library out. You can find much more information, including the aforementioned tutorial, on the Glazed List website.

 

Chaining together multiple list transformations makes it easy to create powerful programs

Java: Creating correct equals and hashCode methods

August 24, 2010 7 comments

Equals and hashcode

Generating correct equals and hashCode methods is hard. There’s an entire chapter or two devoted to it in Joshua Bloch’s Effective Java, a definitive tome for Java developers. Getting these two methods correct is important if you’re going to be using your domain objects within Java collections, particularly hash maps.

As manually generating equals and hashCode methods is difficult and error prone, there are a few different techniques for helping the developer with the process.

We will be using the following simple class to explore this issue:

public class Point {
    private int x;
    private int y;

    public Point(int x, int y) {
      this.x = x;
      this.y = y;
    }

    public int getX() {
      return x;
    }

    public int getY() {
      return y;
    }
}

Library based solution

There is at least one library that supports the generation of equals and hashCode methods, and that is the excellent Apache Commons Lang. There is a good explanation of how to use it here, but here’s a condensed version, from the javadocs with my annotation added in the comments:

// Nick: An example using reflection to determine all of the fields of the object;
// easiest to use but as it uses reflection it will be slower than manually
// including the fields
public boolean equals(Object obj) {
   return EqualsBuilder.reflectionEquals(this, obj);
}

// Nick: An example explicitly choosing which fields to include within the EqualsBuilder
// Note that it still requires some knowledge of creating correct equals methods, so it's not as idiot proof as the previous method
public boolean equals(Object obj) {
  if (obj instanceof MyClass == false) {
    return false;
  }
  if (this == obj) {
    return true;
  }
  MyClass rhs = (MyClass) obj;
  return new EqualsBuilder()
                .appendSuper(super.equals(obj))
                .append(field1, rhs.field1)
                .append(field2, rhs.field2)
                .append(field3, rhs.field3)
                .isEquals();
 }

The HashCodeBuilder works similarly:

public class Person {
   String name;
   int age;
   boolean isSmoker;
   ...



   public int hashCode() {
     // you pick a hard-coded, randomly chosen, non-zero, odd number
     // ideally different for each class
     return new HashCodeBuilder(17, 37).
       append(name).
       append(age).
       append(smoker).
       toHashCode();
   }

   // Nick: Alternatively, for the lazy:
   public int hashCode() {
      return HashCodeBuilder.reflectionHashCode(this);
   }

}

Using the library is a good approach, but it also introduces a dependency that may not be otherwise necessary. There are a lot of good classes in Apache Commons Lang, but if all you are using it for is the EqualsBuilder and ToStringBuilder, you’re probably better off avoiding the dependency. In this case, you can make your IDE do the heavy lifting for you.

IDE based code generation

Given that IDEs like NetBeans and Eclipse do such a good job of automatically creating things like getters/setters, constructors, etc., it’s no surprise that they can be used to generate equals/hashCode methods as well. Unfortunately, they are not perfect, which prompted me to write this post in the first place.

I will be focusing on NetBean’s implementation of the equals/hashCode code generation as of version 6.9 (the most recent version).

When you are in NetBeans and press Ctrl+I, the IDE provides a popup menu with options for methods that it can automatically generate for you.

Generate options in NetBeans 6.9

When you choose the equals() and hashCode() option, you are presented with the following screen (where the variables will differ depending on your class, obviously).

equals() and hashCode() generation dialog

After checking all of the checkboxes and pressing generate, the IDE inserts the following two snippets of code:

@Override
public boolean equals(Object obj) {
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    final Point other = (Point) obj;
    if (this.x != other.x) {
        return false;
    }
    if (this.y != other.y) {
        return false;
    }
    return true;
}

@Override
public int hashCode() {
    int hash = 3;
    hash = 97 * hash + this.x;
    hash = 97 * hash + this.y;
    return hash;
}

Perfect. Great. The IDE has done all the work for you. It’s definitely more verbose than the Apache Commons solution, but at least there are no dependencies introduced into your code. If you change your class so as to introduce more variables you wish to consider for equality and hashCode, you should delete the generated methods and regenerate them.

While this is functional, there are two main problems I have with this dialog:
* Multiple checkboxes
* No linkage between equals/hashCode

I will address each in turn

Multiple checkboxes

There is no means for enabling or disabling all of the fields. Any time there are (potentially) a lot of checkboxes, you should give the user the option to toggle them all at once. You can that the NetBeans designers did just this in the Generate Getters and Setters dialog in NetBeans 6.9.

Generate getter / setters

Here you can see a checkbox next to the Point class name which toggles all of the children nodes’ checkboxes (all of the variables). This is pretty standard UI stuff; here is this pattern at work in GMail and Google Docs.

GMail's select/deselect options Google Doc's select/deselect

This is not the end of the world, as the dialog does support keyboard navigation and toggling of the check boxes via the space bar. It is a bizarre UI feature though, as there is absolutely no indication as to which of the two panes has focus, and thus which checkbox you’re about to toggle. By the fact that I’m familiar with focus traversal, I intuited that tab would shift the focus between the panes but there’s no way a novice would know that and no indication of this. In the following screenshot, note that it’s impossible to tell whether I’m about to toggle the x or the y variable.

What will happen when I press space?

Lack of coupling between the equals/hashCode methods

Usually coupling is considered a bad thing in programming. However, when creating an equals and hashCode methods, it’s vital that the same fields be used in the construction of both methods. For instance, if you use a variable x and y to create the equals methods, you should use exactly the variables x and y while constructing the hashCode method.

Why?

This post from bytes.com does a good job of explaining this:

Overriding the hashCode method.

The contract for the equals method should really have another line saying you must proceed to override the hashCode method after overriding the equals method. The hashCode method is supported for the benefit of hash based collections.

The contract

Again from the specs:

  • Whenever it is invoked on the same object more than once during an execution of an application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

So equal objects must have equal hashCodes. An easy way to ensure that this condition is always satisfied is to use the same attributes used in determining equality in determining the hashCode. You should now see why it is important to override hashCode every time you override equals.

That sentence from the last paragraph sums it up: “An easy way to ensure that this condition is always satisfied is to use the same attributes used in determining equality in determining the hashCode”. Thus it’s clear that the dialog should provide a linkage between the equals/hashCode columns such that toggling the row of one column toggles the corresponding row. Otherwise you can create situations that are guaranteed to violate the contract of equals/hashCode, nullifying the entire point of having the IDE generate these methods for you.

For instance, see the following screen shot:
Violation of contract, allowed by the GUI

The dialog will allow you to continue, blithely creating the erroneous methods, only to manifest itself as subtle bugs later, with no warning. Either the dialog should force you to choose the variables in tandem, or at the very least it should offer a warning that choosing mismatching variables for the equals and hashCode methods can introduce bugs into the program.

Conclusion

I’ve investigated two ways of freeing the developer from the burden of implementing a correct version of equals and hashCode, through the use of Apache Commons Lang and NetBeans IDE. I’ve also detailed problems in the UI design of the dialogs presented for the generation of these two methods from NetBeans.

EDIT:
Thanks to Daniel for bringing Eclipse’s dialog to my attention. Eclipse's dialog
As you can see, they do not separate out the equals/hashCode, which makes a lot more sense to me.

XStream introduction and Java Collections serialization problem workaround

July 13, 2010 3 comments

XStream is an open-source Java library for converting objects to and from XML.  I’ll introduce how to use the library, show a problem I ran into that was a bit difficult to track down, as well as a workaround.

Library example

Imagine we are modeling a library in XML.  This will be much simplified for the purposes of illustration.  A sample XML library document might look like the following:

<library>
  <books>
    <book>
      <title>The Talent Code: Greatness Isn't Born. It's Grown. Here's How.</title>
      <author>Daniel Coyle</author>
    </book>
  </books>
</library>

Let’s write some Java classes to represent this hierarchy.

public class Library {
    public List<Book> books;

    public Library(List<Book> books) {
        this.books = books;
    }

    @Override
    public String toString() {
        StringBuilder b = new StringBuilder();
        b.append("Library with " + books.size() + " books:\n");
        for (Book book : books) {
            b.append(book.toString());
            // Skip a line
            b.append("\n");
        }
        return b.toString();
    }

    public static class Book {
        private String title;
        private String author;

        public Book(String title, String author) {
            this.title = title;
            this.author = author;
        }

        @Override
        public String toString() {
            return "\"" + title + "\" by " + author;
        }
    }

    public static void main(String[] args) {
        List<Book> books = new ArrayList<Book>();
        books.add(new Book("The Talent Code: Greatness Isn't Born. It's Grown. Here's How", "Daniel Coyle"));

        Library lib = new Library(books);
        // Handles conversion of our objects into XML
        XStream stream = new XStream(new DomDriver());

        String xml = stream.toXML(lib);
        // fromXML returns a general Object; need to cast it into a Library
        Library lib2 = (Library) stream.fromXML(xml);

        System.out.println(xml);
        System.out.println(lib);
        System.out.println(lib2);
        System.out.println("Libraries equal: " + lib.toString().equals(lib2.toString()));
    }
}

This is not production worthy by any means; in general comparing the string representations of objects to test for equality is a very bad idea.  But writing a foolproof equals method is very difficult and irrelevant to the point of this post.  Here is the output produced by running the program:

<Library>
  <books>
    <Library_-Book>
      <title>The Talent Code: Greatness Isn&apos;t Born. It&apos;s Grown. Here&apos;s How</title>
      <author>Daniel Coyle</author>
    </Library_-Book>
  </books>
</Library>
Library with 1 books:
"The Talent Code: Greatness Isn't Born. It's Grown. Here's How" by Daniel Coyle

Library with 1 books:
"The Talent Code: Greatness Isn't Born. It's Grown. Here's How" by Daniel Coyle
Libraries equal: true

This is somewhat close to what we want, but not exactly.  (Note that the &apos; stuff in the middle of the title is not a mistake; I was imprecise by including apostrophes in the original XML example.  See here for an explanation of why apostrophes are treated specially in XML). Let’s start by aliasing some of the tag names.  The relevant change is as follows:

XStream stream = new XStream(new  DomDriver());
stream.alias("library", Library.class);
stream.alias("book",  Book.class);

Output:

<library>
  <books>
    <book>
      <title>The Talent Code: Greatness Isn&apos;t Born. It&apos;s Grown. Here&apos;s How</title>
      <author>Daniel Coyle</author>
    </book>
  </books>
</library>

Initial success

OK, looks like things are working!  But the way of constructing the list is not how I normally create lists.  Let’s make sure the serialization works for alternative means of List construction.

Book talentCode =  new Book("The Talent Code: Greatness Isn't Born. It's Grown. Here's  How", "Daniel Coyle");
Library lib = new  Library(Arrays.asList(talentCode));

For you who haven’t seen the use of Arrays.asList, it’s a method to convert an array (or varargs) into a List.  It’s important for compatibility between array based and collection based frameworks.  The corresponding method in the opposite direction, from Collections to arrays is the Collections.toArray.  For the truly nerdy, here’s an interesting discussion of possible implications of calling toArray in a multithreaded environment.
Anyways, here’s the output.

<library>
  <books class="java.util.Arrays$ArrayList">
    <a class="book-array">
      <book>
        <title>The Talent Code: Greatness Isn&apos;t Born. It&apos;s Grown. Here&apos;s How</title>
        <author>Daniel Coyle</author>
      </book>
    </a>
  </books>
</library>
Library with 1 books:
"The Talent Code: Greatness Isn't Born. It's Grown. Here's How" by Daniel Coyle

Library with 1 books:
"The Talent Code: Greatness Isn't Born. It's Grown. Here's How" by Daniel Coyle

Libraries equal: true

Hmmm.  The XML serialization/deserialization still works but it’s sure not pretty.  What’s going on here?  Why did it print “books” earlier but now it throws in all that ugly stuff about “java.util.Arrays$ArrayList”?

The answer is that XStream has a powerful set of converters that handle mapping Java constructs to and from XML.  The relevant converter is the CollectionConverter.  Here is the JavaDoc description, and the solution to the mystery:

Converts most common Collections (Lists and Sets) to XML, specifying a nested element for each item.

Supports java.util.ArrayList, java.util.HashSet, java.util.LinkedList, java.util.Vector and java.util.LinkedHashSet.

The problem is that creating a List via the Arrays.toList method does not create an instance of any of these classes.  Instead, it returns an instance of an inner class named ArrayList (not to be confused with the java.util.ArrayList).  Read the source of the java.util.Arrays class if you are interested in the implementation details.

What’s the workaround?  Don’t blindly accept the list of books that is passed into the constructor.  Instead, make a defensive copy.

public List<Book> books = new  ArrayList<Book>();

public Library(List<Book>  books) {
      this.books.addAll(books);
 }

Sure enough, this fixes the problem, as the actual class of the list of books is now one that the converter supports.  Alternatively we could change the implementation of CollectionConverter to support the Arrays$ArrayList class, but it’s probably not worth it.

Conclusion

I introduced the XStream library for Java which handles the conversion of objects to and from XML.  I presented a very simple Java example of a Library and its collection of books, as well as a problem that arises from the way XStream converts collections into XML.

Excellent video detailing motivation behind open source

May 23, 2010 1 comment

This has been all over Twitter, so I apologize if this isn’t new to you. I absolutely love the fact that this video had as a visual not a boring set of PowerPoint slides, but instead a beautifully illustrated backdrop that was both informative, clever, and funny (the BTTF reference made me laugh).

This comes to a slightly different conclusion than that of The Cathedral & the Bazaar: Musings on Linux and Open Source by an Accidental Revolutionary, one of the more popular books explaining the open source movement.  In that work, the author Eric Raymond puts a lot more emphasis on the importance of recognition as a motivating factor for open source; it’s not just the fact that you are contributing to a worthwhile cause, but the fact that your name is attached to the work.

Obviously that book was written much earlier than this video was produced, but I wonder what the author would think of it.

Categories: open source