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:
All the Swing widgets are constructed on the event dispatch thread.
We use the SwingUtilities.invokeLater() utility method to run
to accomplish this.
The constructors for our transformations SortedList,
TextFilterList require that we acquire we obtain the
source EventList's locks to execute.
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]); } }