Multithreading our IssuesBrowser

Adding multithreading support to our IssuesBrowser is straightforward. We create a background thread that loads issues from a file or web service and populates our issues list with the result. Our XML parser uses a callback issueLoaded(), so we can show each issue as it arrives rather than waiting for the complete set.

import java.io.*;
// a simple issues library
import ca.odell.issuezilla.*;
// glazed lists
import ca.odell.glazedlists.*;

/**
 * Loads issues on a background thread.
 * 
 * @author <a href="mailto:jesse@odel.on.ca">Jesse Wilson</a>
 */
public class IssuesLoader implements Runnable, IssuezillaXMLParserHandler {

  /** the issues list */
  private EventList issues = new BasicEventList();
  
  /** where issues shall be found */
  private String file;
  
  /**
   * Get the list that issues are being loaded into.
   */
  public EventList getIssues() {
    return issues;
  }
  
  /**
   * Load the issues from the specified file.
   */
  public void load(String file) {
    this.file = file;
    
    // start a background thread
    Thread backgroundThread = new Thread(this);
    backgroundThread.setName("Issues from " + file);
    backgroundThread.setDaemon(true);
    backgroundThread.start();
  }
  
  /**
   * When run, this fetches the issues from the issues URL and refreshes
   * the issues list.
   */
  public void run() {
    // load some issues
    try {
      IssuezillaXMLParser parser = new IssuezillaXMLParser();
      InputStream issuesInStream = new FileInputStream(file);
      parser.loadIssues(issuesInStream, this);
      issuesInStream.close();
    } catch(IOException e) {
      e.printStackTrace();
      return;
    }
  }
  
  /**
   * Handles a loaded issue.
   */
  public void issueLoaded(Issue issue) {
    issues.getReadWriteLock().writeLock().lock();
    try {
      issues.add(issue);
    } finally {
      issues.getReadWriteLock().writeLock().unlock();
    }
  }
}

We also modified our IssuesBrowser to be threadsafe. We performed some necessary changes:

import java.util.*;
import javax.swing.*;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
// a simple issues library
import ca.odell.issuezilla.*;
// glazed lists
import ca.odell.glazedlists.*;
import ca.odell.glazedlists.swing.*;

/**
 * An IssueBrowser is a program for finding and viewing issues.
 * 
 * @author <a href="mailto:jesse@odel.on.ca">Jesse Wilson</a>
 */
public class IssuesBrowser implements Runnable {
  
  /** reads issues from a stream and populates the issues event list */
  private IssuesLoader issueLoader = new IssuesLoader();
  
  /** event list that hosts the issues */
  private EventList issuesEventList = issueLoader.getIssues();

  /**
   * Create an IssueBrowser for the specified issues.
   */
  public IssuesBrowser(String file) {
    SwingUtilities.invokeLater(this);
    issueLoader.load(file);
  }
  
  /**
   * Display a frame for browsing issues. This should only be run on the Swing
   * event dispatch thread.
   */
  public void run() {
    issuesEventList.getReadWriteLock().readLock().lock();
    try {
      // create the transformed models
      SortedList sortedIssues = new SortedList(issuesEventList, new IssueComparator());
      UsersSelect usersSelect = new UsersSelect(sortedIssues);
      FilterList userFilteredIssues = new FilterList(sortedIssues, usersSelect);
      TextFilterList textFilteredIssues = new TextFilterList(userFilteredIssues, new IssueTextFilterator());
    
      // create the issues table
      EventTableModel issuesTableModel = new EventTableModel(textFilteredIssues, new IssueTableFormat());
      JTable issuesJTable = new JTable(issuesTableModel);
      TableComparatorChooser tableSorter = new TableComparatorChooser(issuesJTable, sortedIssues, true);
      JScrollPane issuesTableScrollPane = new JScrollPane(issuesJTable);

      // create the users list
      JScrollPane usersListScrollPane = new JScrollPane(usersSelect.getJList());
      
      // create the panel
      JPanel panel = new JPanel();
      panel.setLayout(new GridBagLayout());
      panel.add(new JLabel("Filter: "), new GridBagConstraints(...));
      panel.add(textFilteredIssues.getFilterEdit(), new GridBagConstraints(...));
      panel.add(new JLabel("Reported By: "), new GridBagConstraints(...));
      panel.add(usersListScrollPane, new GridBagConstraints(...));
      panel.add(issuesTableScrollPane, new GridBagConstraints(...));
      
      // create a frame with that panel
      JFrame frame = new JFrame("Issues");
      frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      frame.setSize(540, 380);
      frame.getContentPane().add(panel);
      frame.show();
    } finally {
      issuesEventList.getReadWriteLock().readLock().unlock();
    }
  }
  
  /**
   * Launch the IssuesBrowser from the commandline.
   */
  public static void main(String[] args) {
    if(args.length != 1) {
      System.out.println("Usage: IssuesBrowser <file>");
      return;
    }
    
    // create the browser
    IssuesBrowser browser = new IssuesBrowser(args[0]);
  }
}