- Java Reference Guide
- Overview
- Table of Contents
- J2SE: Standard Java
- Java Windows NT Services
- Apache Velocity
- Advanced J2SE
- J2SE 1.5.0: "Tiger"
- Java SE 6
- Core Computer Science Principles in Java (Data Structures)
- Annotations
- Java Generics
- Java New I/O
- Java Sound
- Java Applets
- JavaFX
- Java SE Threading
- Resource Management Using Semaphores
- Java Atomic Operations
- JavaTemplate Pages
- Executing Templates with the JtpExecutor
- Java Cryptography Extensions (JCE)
- Java Database Connectivity (JDBC) API
- Jakarta Commons - Net Class Library
- Jakarta Commons HttpClient
- Apache POI
- Regular Expressions
- JavaMail
- Cool Tools
- Building an Really Simple Syndication (RSS) Java App
- Embedding JavaScript in Java with Rhino
- Logging with Log4J
- Inside Swing
- Swing Components
- SwingX
- Swing Styled Documents
- Web Rendering in Java Swing Applications
- Java Look-and-Feel Graphics Repository
- Java Media Framework
- Quicktime for Java
- Media in Java Review 2008
- Graphs and Charts
- Holiday Special: Electronic Greeting Card
- Media Framework: Presenter Application
- Standard Widget Toolkit
- JFace
- Java Performance Tuning
- J2EE Performance Tuning
- Caches and Pools
- Java Caching System
- Java Compression and Decompression
- Obfuscating Java Applications
- Continuous Integration New
- Load Testing
- Tomcat Clustering
- High Scalability with Terracotta New
- Enterprise Java Testing
- Automated Unit Testing with JUnit and Ant
- Unit Testing: Tips From The Trenches
- Custom Ant Tasks
- Extensible Markup Language (XML)
- Java Web Technologies
- Web Frameworks
- Struts 2
- Wicket
- JavaServer Faces
- Distributed Programming / RMI
- Servlet Filters
- Building a Robust Java Server
- J2EE: Enterprise Java
- Spring
- Spring 3
- Java Design Patterns
- Model-Driven Architecture
- XDoclet
- Hibernate
- Project Backup
- J2EE Project: Hands-On
- Enterprise Java Beans (EJB) 3.0
- Disaster Recovery
- Java Management Extensions (JMX)
- Service-Oriented Architecture
- Web Services
- RESTful Web Services
- Project: Building a Web Photo Gallery
- J2ME: Micro Java
- Specialized J2ME
- Optional Packages
- Other Java Technologies
- Derivatives and Competitors
- Java, Engineered for Integration
- Additional Resources
- The World of Java Tools
- Building Java Applications with Ant
- Managing Java Build Lifecycles with Maven
- Source Control with Subversion
- Inversion of Control and Dependency Injection
- Certification
- Roadmap: Becoming an Enterprise Java Developer
- Roadmap: Becoming an Enterprise Java Developer in 2007
- The Business of Enterprise Software
- JavaOne 2006
- JavaOne 2007
- JavaOne 2008 Wrap-Up
- JavaOne 2009 Wrap-Up
- How to Survive in a Turbulent Job Market
- How to Hire the Best Talent
- Unified Modeling Language (UML)
- Cloud Computing
- Enterprise Java in 2008 and Beyond
- Predictions for 2018
Displaying Articles
Last updated Aug 20, 2004.
The next step in our RSS reader's evolution is displaying articles and article descriptions. To accomplish this, we need to do a few things:
Create a listener that will handle double-clicks on our JTable signally the selection of a news item to read
Popup a new frame that displays information about the article as well as its description
Optionally launch a browser that will display the actual article to the user
Handling a double-click event on a JTable is not as straightforward as you might assume. There is no event handler announcing selected rows; rather, we must handle generic mouse events on the JTable itself. Once a mouseClicked() mouse event is generated, we can check the click-count and then ask the JTable to resolve the physical point that the mouse was clicked to a row index.
Listing 12 shows the changes made to the RSSManagerFrame to support handling double-clicks on its table.
Listing 12. RSSFrameManager.java
...
public class RSSManagerFrame extends JInternalFrame implements TreeSelectionListener, MouseListener
{
...
private Set listeners = new TreeSet();
public RSSManagerFrame( Element rootElement )
{
...
// Add ourselves as a mouse listener to the table
this.table.addMouseListener( this );
...
}
...
/**
* Invoked when the mouse button has been clicked (pressed
* and released) on a component.
*/
public void mouseClicked(MouseEvent e)
{
if( e.getClickCount() == 2 )
{
int row = this.table.rowAtPoint( e.getPoint() );
if( row != -1 )
{
// User double clicked a row in the table
String title = this.tableModel.getTitle( row );
URL link = this.tableModel.getLink( row );
System.out.println( link + ": " + title );
this.fireShowLink( this.tableModel.getItem( row ) );
}
}
}
/**
* Invoked when a mouse button has been pressed on a component.
*/
public void mousePressed(MouseEvent e)
{
}
/**
* Invoked when a mouse button has been released on a component.
*/
public void mouseReleased(MouseEvent e)
{
}
/**
* Invoked when the mouse enters a component.
*/
public void mouseEntered(MouseEvent e)
{
}
/**
* Invoked when the mouse exits a component.
*/
public void mouseExited(MouseEvent e)
{
}
public void addRSSActionListener( RSSActionListener l )
{
this.listeners.add( l );
}
public void removeRSSActionListener( RSSActionListener l )
{
this.listeners.remove( l );
}
public void fireShowLink( ItemIF item )
{
RSSActionEvent e = new RSSActionEvent( item );
for( Iterator i=this.listeners.iterator(); i.hasNext(); )
{
RSSActionListener l = ( RSSActionListener )i.next();
l.showLink( e );
}
}
}
All of the action for handling the double-click takes place in the mouseClicked() method. In this method, we validate that the mouse was clicked twice and then call the JTable's rowAtPoint() method. This method takes a Point on the table and returns then index of the row that contains that Point. If the mouse was clicked over the table, but not over a valid row, then it returns -1. We use that row index to retrieve the Informa ItemIF from our table model (I added that method later, see the included source code.)
Once we identify that a double-click occurred, our next task, before launching a internal frame, is to tell our parent class that the event occurred (so that it can create the internal frame and add it to the desktop.)
This behavior is modeled after the traditional "Event Delegation Model" that Swing uses. The Event Delegation Model consists of the following elements:
An Event Listener interface
An Event class
A component that can generate an event
In this case, I created an RSSActionListener interface, an RSSActionEvent class, and modified the RSSManagerFrame to generate RSSActionEvents. Listings 13 and 14 show the code for the RSSActionListener and RSSActionEvent classes, respectively.
Listing 13. RSSActionListener.java
package com.javasrc.rss.event;
public interface RSSActionListener
{
public void showLink( RSSActionEvent event );
}
Listing 14. RSSActionEvent.java
package com.javasrc.rss.event;
import java.util.*;
import java.net.*;
// Import the Informa Libraries
import de.nava.informa.core.*;
public class RSSActionEvent extends EventObject
{
private ItemIF item;
public RSSActionEvent( ItemIF item )
{
super( item );
this.item = item;
}
public URL getLink()
{
return this.item.getLink();
}
public ItemIF getItem()
{
return this.item;
}
}
The RSSActionListener has only a single method: showLink(). This method is passed an RSSActionEvent that basically wraps an ItemIF instance and provides access to it.
Now that the JRSS class has been asked to show a specific link, it creates an instance of the HTMLViewer class (to display the article summary information) and adds it to the JDesktopPane. Here's how it handles its showLink() method:
public void showLink(RSSActionEvent event)
{
HTMLViewer viewer = new HTMLViewer( event.getItem(), browser );
desktop.add( viewer );
position += INDENT;
viewer.setLocation( position, position );
desktop.setPosition( viewer, 0 );
}
The showLink() method creates an instance of the HTMLViewer class, passing it the ItemIF from the RSSActionEvent as well as the fully qualified path to the user's configured browser (more on that coming). It then adds the viewer to the JDesktopPane by calling its add() method and does two things:
Offsets the window so that it won't be right on top of the RSSManagerFrame by calling setLocation()
Brings it to the top of the desktop stack by calling setPosition() for the viewer to position 0
The HTMLViewer class completes the equation.
A little history on the naming of this class and the original intent of the class: originally, I wanted the class to display the contents of the article itself. Java has a JEditorPane to which you can pass a URL, and it will create an HTMLDocument to render the contents of the page. I originally coded it this way. Although it did display the contents of the URL, it was formatted horribly; if you know of a more robust HTML presentation library, please E-mail me! Anyway, I refactored the class to present summary information about the article, a description of the article (from the RSS feed), and if the user configures a browser then the class can launch the browser passing it the URL to the article.
Listing 15 shows the code for the HTMLViewer class.
Listing 15. HTMLViewer.java
package com.javasrc.rss.gui;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.*;
import java.io.*;
// Import the Informa Libraries
import de.nava.informa.core.*;
// Import our classes
import com.javasrc.gui.*;
public class HTMLViewer extends JInternalFrame implements ActionListener
{
private ItemIF item;
private JTextArea text;
private JButton readArticle = new JButton( "Read Article" );
private String browser;
public HTMLViewer( ItemIF item, String browser )
{
super( item.getTitle(), true, true, true, true );
try
{
// Build the top panel
JPanel topPanel = new JPanel( new GridLayout( 3, 2 ) );
topPanel.add( GUIUtils.getRJPanel( "Channel:" ) );
String channel = item.getChannel().getTitle();
topPanel.add( GUIUtils.getLJPanel( channel ) );
topPanel.add( GUIUtils.getRJPanel( "Title:" ) );
topPanel.add( GUIUtils.getLJPanel( item.getTitle() ) );
java.util.Date date = item.getDate();
if( date != null )
{
topPanel.add( GUIUtils.getRJPanel( "Date:" ) );
topPanel.add( GUIUtils.getLJPanel( date.toString() ) );
}
this.getContentPane().add( topPanel, BorderLayout.NORTH );
// Initialize the editor pane
this.item = item;
String description = item.getDescription();
text = new JTextArea( description );
text.setLineWrap( true );
text.setWrapStyleWord( true );
this.getContentPane().add( new JScrollPane( text ) );
// Add link button
if( browser != null )
{
this.browser = browser;
readArticle.addActionListener( this );
this.getContentPane().add( GUIUtils.getRJPanel( readArticle ), BorderLayout.SOUTH );
}
// Show the frame
this.setVisible( true );
this.setSize( 600, 400 );
}
catch( Exception e )
{
e.printStackTrace();
}
}
public void actionPerformed( ActionEvent e )
{
if( e.getSource() == this.readArticle )
{
String link = this.item.getLink().toString();
if( link != null && link.length() > 0 )
{
try
{
Runtime rt = Runtime.getRuntime();
Process p = rt.exec( new String[] { browser, link } );
}
catch( Exception ex )
{
ex.printStackTrace();
}
}
}
}
}
The HTMLViewer class is broken into three regions: the top shows information about the article, the center shows the description of the article, and the bottom has a JButton that launches a browser to the link to which the article points. With any background in using the Informa libraries, the creation of the panels is pretty straightforward; notwithstanding a helper class that I built to create right and left justified panels (shown in listing 16).
Listing 16. GUIUtils.java
package com.javasrc.gui;
import java.awt.*;
import javax.swing.*;
public class GUIUtils
{
public static JPanel getLJPanel( String label )
{
return GUIUtils.getLJPanel( new JLabel( label ) );
}
public static JPanel getRJPanel( String label )
{
return GUIUtils.getRJPanel( new JLabel( label ) );
}
public static JPanel getCenteredPanel( String label )
{
return GUIUtils.getCenteredPanel( new JLabel( label ) );
}
public static JPanel getLJPanel( Component c )
{
JPanel panel = new JPanel( new FlowLayout( FlowLayout.LEFT ) );
panel.add( c );
return panel;
}
public static JPanel getRJPanel( Component c )
{
JPanel panel = new JPanel( new FlowLayout( FlowLayout.RIGHT ) );
panel.add( c );
return panel;
}
public static JPanel getCenteredPanel( Component c )
{
JPanel panel = new JPanel( new FlowLayout( FlowLayout.CENTER ) );
panel.add( c );
return panel;
}
}
The next bit of interesting code is the code that handles launching a Web browser. Because the user-interface only has a single JButton and it's the sole browser launcher process, I opted to use the traditional event-handling and not create an action. In your own applications, if you add a menu option and toolbar button, you will want to refactor the code to create an action.
From listing 15, the actionPerformed() method implements the browser launching code. It makes use of the java.lang.Runtime class's exec() method. This version of the exec() method accepts a String array containing the executable to launch as the first parameter and then its arguments in subsequent parameters. In this case, we launch the browser that we read in from our configuration file and pass it the link for the article as its parameter. A sample is shown in listing 17.
Listing 17. jrss.xml
<jrss>
<config>
<browser>C:\Program Files\Internet Explorer\iexplore.exe</browser>
</config>
<categories>
<category name="J2EE">
<channel name="InformIT :: Java Reference Guide" />
<channel name="TSS" />
</category>
<category name="News">
<channel name="Yahoo Top Stories" />
<channel name="Yahoo World" />
<channel name="Yahoo Politics" />
<channel name="Yahoo Business" />
<channel name="Yahoo Technology" />
<channel name="Yahoo Entertainment" />
<channel name="Yahoo Sports" />
</category>
</categories>
<channels>
<channel name="InformIT :: Java Reference Guide" url="http://www.informit.com/guides/guide_rss.asp?g=java" />
<channel name="TSS" url="http://www.theserverside.com/rss/theserverside-rss2.xml" />
<channel name="Yahoo Top Stories" url="http://rss.news.yahoo.com/rss/topstories" />
<channel name="Yahoo World" url="http://rss.news.yahoo.com/rss/world" />
<channel name="Yahoo Politics" url="http://rss.news.yahoo.com/rss/politics" />
<channel name="Yahoo Business" url="http://rss.news.yahoo.com/rss/business" />
<channel name="Yahoo Technology" url="http://rss.news.yahoo.com/rss/tech" />
<channel name="Yahoo Entertainment" url="http://rss.news.yahoo.com/rss/entertainment" />
<channel name="Yahoo Sports" url="http://rss.news.yahoo.com/rss/sports" />
</channels>
</jrss>
This has been a very quick overview of the key components of the RSS reader. Please download the code, support libraries (Informa and JDOM), compile it, and see how it works for you.
Summary
This short series of articles began as an exploration of RSS and how to use an open source project (Informa) to abstract the details of reading RSS feeds. The end result is a hodgepodge of Swing components to build a useful GUI around it. RSS is simple to understand, but the details of reading all of the different supported versions may deter you from implementing a solution around it. The Informa library provides an RSS version agnostic interface to read the popular RSS feeds.
The application presented in this project is far from complete, but it is a good starting point if you are interested in developing an RSS reader. Through this project we touched on developing Multiple Document Interface (MDI) applications using JDesktopPane and JInternalFrame, using trees and tables, hacking tables to identify double-clicks on its rows, developing a custom implementation of the event delegation model, and launching external processes.
If you are interested in continuing this project, I urge you to add menus and toolbars to the main application as well as to the internal frames and (probably most important) provide an interface to allow the user to customize his groups and categories. Good luck!




Account Sign In
View your cart