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]); } }