DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • AI: Do You Trust It?
  • Spring Boot Application With Spring REST and Spring Data MongoDB
  • Accelerate Innovation by Shifting Left FinOps: Part 4
  • Building A Simple AI Application in 2023 for Fun and Profit

Trending

  • Cookies Revisited: A Networking Solution for Third-Party Cookies
  • How to Configure and Customize the Go SDK for Azure Cosmos DB
  • Contextual AI Integration for Agile Product Teams
  • Optimizing Integration Workflows With Spark Structured Streaming and Cloud Services
  1. DZone
  2. Data Engineering
  3. Data
  4. Building a Simple RSS Client in Pivot

Building a Simple RSS Client in Pivot

By 
Greg Brown user avatar
Greg Brown
·
Apr. 23, 09 · News
Likes (0)
Comment
Save
Tweet
Share
9.5K Views

Join the DZone community and get the full member experience.

Join For Free

A few months ago, this article by Andrew Trice inspired me to write a demo application to see how well Pivot would handle very large tabular data sets of up to one million rows (the results are here). Mr. Trice has again inspired me, this time to see how easy it might be to to write a Pivot application that consumes an RSS feed and presents the data in a list view using a custom item renderer. This article describes the results.

The Demo Application

The following image shows a screen shot of the demo application, which retrieves RSS feed data from Javalobby. A live example is available here (it requires Java 6 Update 10 or later; see notes at bottom of page). The application contains a single ListView component in a ScrollPane that is used to present the news data. A custom list item renderer is used to display the item's title, categories, and submitter.

The WTKX source for the demo's UI is as follows:

<Border styles="{padding:0, color:10}"
xmlns:wtkx="http://incubator.apache.org/pivot/wtkx/1.1"
xmlns:effects="pivot.wtk.effects"
xmlns:rss="pivot.demos.rss"
xmlns="pivot.wtk">
<content>
<CardPane wtkx:id="cardPane" selectedIndex="0">
<Label wtkx:id="statusLabel" text="Loading..."
styles="{horizontalAlignment:'center', verticalAlignment:'center'}"/>
<ScrollPane horizontalScrollBarPolicy="fill">

<view>
<ListView wtkx:id="feedListView"/>
</view>
</ScrollPane>
</CardPane>
</content>

</Border>

It's pretty straightforward: a root Border component contains a CardPane containing two sub-panes. The first is a status label that is used to provide feedback to the user while the feed is being loaded. The second is a ScrollPane containing the actual list view used to present the feed data.

The Java source is a bit more involved. It is broken into two files: one contains the main application class, and the other contains an adapter class that is used to wrap an XML node list such that it can be used as the data model for the list view. The application class contains several inner classes that are used to facilitate loading and presenting the data, as well as opening the articles in an external browser window when the user double-clicks on the list.

The Main Application Class

The application's constructor is shown below:

public RSSFeedDemo() {
// Create an XPath instance
xpath = XPathFactory.newInstance().newXPath();

// Set the namespace resolver
xpath.setNamespaceContext(new NamespaceContext() {
public String getNamespaceURI(String prefix) {
String namespaceURI;
if (prefix.equals("dz")) {
namespaceURI = "http://www.developerzone.com/modules/dz/1.0";
} else {
namespaceURI = XMLConstants.NULL_NS_URI;
}

return namespaceURI;
}

public String getPrefix(String uri) {
throw new UnsupportedOperationException();
}

public Iterator getPrefixes(String uri) {
throw new UnsupportedOperationException();
}
});
}

Using the newInstance() factory method, it obtains an instance of javax.xml.xpath.XPath that is later used to retrieve element values from the returned data. A namespace resolver is then set on the XPath, allowing it to map the "dz" prefix to the "http://www.developerzone.com/modules/dz/1.0" namespace URI.

The startup() method is defined as follows:

public void startup(Display display, Dictionary properties)
throws Exception {
WTKXSerializer wtkxSerializer = new WTKXSerializer();
window = new Window((Component)wtkxSerializer.readObject(getClass().getResource("rss_feed_demo.wtkx")));

feedListView = (ListView)wtkxSerializer.getObjectByName("feedListView");
feedListView.setItemRenderer(new RSSItemRenderer());
feedListView.getComponentMouseButtonListeners().add(new FeedViewMouseButtonHandler());

final CardPane cardPane = (CardPane)wtkxSerializer.getObjectByName("cardPane");
final Label statusLabel = (Label)wtkxSerializer.getObjectByName("statusLabel");

LoadFeedTask loadFeedTask = new LoadFeedTask();
loadFeedTask.execute(new TaskListener() {
public void taskExecuted(Task task) {
feedListView.setListData(new NodeListAdapter(task.getResult()));
cardPane.setSelectedIndex(1);
}

public void executeFailed(Task task) {
statusLabel.setText(task.getFault().toString());
}
});

window.setMaximized(true);
window.open(display);
}

It loads the UI from the WTKX file, sets it as the content of a decorationless window, and obtains references to some of the components defined in the file. It assigns an instance of RSSItemRenderer as the item renderer for the list view and attaches an instance of FeedViewMouseButtonHandler as a mouse button listener on the list view (these classes are discussed in more detail below). It then creates an instance of LoadFeedTask, executes the task, and opens the window.

LoadFeedTask

LoadFeedTask is a private inner class that is used to load the feed data on a background thread so the UI doesn't block while it is being loaded. It is defined as follows:

private class LoadFeedTask extends IOTask<NodeList> {
public NodeList execute() throws TaskExecutionException {
NodeList itemNodeList = null;

DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder;
try {
documentBuilder = documentBuilderFactory.newDocumentBuilder();
} catch(ParserConfigurationException exception) {
throw new TaskExecutionException(exception);
}

Document document;
try {
document = documentBuilder.parse(FEED_URI);
} catch(IOException exception) {
throw new TaskExecutionException(exception);
} catch(SAXException exception) {
throw new TaskExecutionException(exception);
}

try {
itemNodeList = (NodeList)xpath.evaluate("/rss/channel/item",
document, XPathConstants.NODESET);
} catch(XPathExpressionException exception) {
// No-op
}

return itemNodeList;
}
}

The execute() method loads an XML document from the feed URI and then uses the XPath object to obtain a node list containing the item elements, which it returns. Upon successful execution, the taskExecuted() method of the task listener is called. This method wraps the returned node list in an instance of NodeListAdapter, which is used to adapt the contents of the NodeList for consumption by the ListView (the source code for this class is available here).

If the load fails, the resulting execption is displayed in the status label.

RSSItemRenderer

The RSSItemRenderer class prepares the feed data for presentation in the list view. It implements the ListView.ItemRenderer interface and extends a vertical FlowPane, which it uses to arrange Label instances containing each article's title, categories, and submitter (note that, while this implementation creates the flow pane's layout programmatically, it could also have been defined in WTKX):

private class RSSItemRenderer extends FlowPane implements ListView.ItemRenderer {
private Label titleLabel = new Label();
private Label categoriesHeadingLabel = new Label("subject:");
private Label categoriesLabel = new Label();
private Label submitterHeadingLabel = new Label("submitter:");
private Label submitterLabel = new Label();

public RSSItemRenderer() {
super(Orientation.VERTICAL);

getStyles().put("padding", new Insets(2, 2, 8, 2));

add(titleLabel);

FlowPane categoriesFlowPane = new FlowPane();
add(categoriesFlowPane);

categoriesFlowPane.add(categoriesHeadingLabel);
categoriesFlowPane.add(categoriesLabel);

FlowPane submitterFlowPane = new FlowPane();
add(submitterFlowPane);

submitterFlowPane.add(submitterHeadingLabel);
submitterFlowPane.add(submitterLabel);
}

...

The render method obtains the font and color styles from the component and applies them to its internal labels. It then sets the contents of the labels by extracting the element content from the current node.

    ...

public void render(Object item, ListView listView, boolean selected,
boolean checked, boolean highlighted, boolean disabled) {
// Render styles
Font labelFont = (Font)listView.getStyles().get("font");
Font largeFont = labelFont.deriveFont(Font.BOLD, 14);
titleLabel.getStyles().put("font", largeFont);
categoriesLabel.getStyles().put("font", labelFont);
submitterLabel.getStyles().put("font", labelFont);

Color color = null;
if (listView.isEnabled() && !disabled) {
if (selected) {
if (listView.isFocused()) {
color = (Color)listView.getStyles().get("selectionColor");
} else {
color = (Color)listView.getStyles().get("inactiveSelectionColor");
}
} else {
color = (Color)listView.getStyles().get("color");
}
} else {
color = (Color)listView.getStyles().get("disabledColor");
}

if (color instanceof Color) {
titleLabel.getStyles().put("color", color);
categoriesHeadingLabel.getStyles().put("color", color);
categoriesLabel.getStyles().put("color", color);
submitterHeadingLabel.getStyles().put("color", color);
submitterLabel.getStyles().put("color", color);
}

// Render data
if (item != null) {
Element itemElement = (Element)item;

try {
String title = (String)xpath.evaluate("title", itemElement, XPathConstants.STRING);
titleLabel.setText(title);

String categories = "";
NodeList categoryNodeList = (NodeList)xpath.evaluate("category", itemElement,
XPathConstants.NODESET);
for (int j = 0; j < categoryNodeList.getLength(); j++) {
Element categoryElement = (Element)categoryNodeList.item(j);
String category = categoryElement.getTextContent();
if (j > 0) {
categories += ", ";
}

categories += category;
}

categoriesLabel.setText(categories);

String submitter = (String)xpath.evaluate("dz:submitter/dz:username", itemElement,
XPathConstants.STRING);
submitterLabel.setText(submitter);
} catch(XPathExpressionException exception) {
System.err.println(exception);
}
}
}
}

FeedViewMouseButtonHandler

The FeedViewMouseButtonHandler class handles double-clicks on the list view. Clicking the list view once stores the index that was clicked (in case the user moves the mouse before a double-click occurs), and the selected item is opened on the double-click:

private class FeedViewMouseButtonHandler extends ComponentMouseButtonListener.Adapter {
private int index = -1;

@Override
public boolean mouseClick(Component component, Mouse.Button button, int x, int y, int count) {
if (count == 1) {
index = feedListView.getItemAt(y);
} else if (count == 2
&& feedListView.getItemAt(y) == index) {
Element itemElement = (Element)feedListView.getListData().get(index);

try {
String link = (String)xpath.evaluate("link", itemElement, XPathConstants.STRING);
BrowserApplicationContext.open(new URL(link));
} catch(XPathExpressionException exception) {
System.err.print(exception);
} catch(MalformedURLException exception) {
System.err.print(exception);
}
}

return false;
}
};

Summary

So, building an RSS reader in Pivot was fairly straightforward. The XPath APIs included in the Java platform are very efficient for retrieving and parsing the feed data, and Pivot makes it easy to wrap the returned XML data in a list model and customize it's presentation in the list view.

However, one important lesson learned is that that Java's XML factory methods don't play nicely with applets. They rely on the JAR service provider architecture and will repeatedly look for service descriptor files on the applet's classpath, resulting in many unnecessary requests to the web server. The only way to prevent this is to set the codebase_lookup applet parameter to false:

<param name="codebase_lookup" value="false">

This works, but it is an ugly workaround - there are certainly valid use cases for using both codebase lookup and XML parsing in an applet; for example, an applet that parses XML that is dynamically generated from a servlet on the applet's classpath. Hopefully Sun (or Oracle) will address this issue in a future JVM update.

Also note that this demo takes advantage of Java 6 Update 10's new support for crossdomain.xml files. This handy feature allows an unsigned applet to make a network connection to a server outside of its origin, provided it was loaded from an approved URL. See this article for more information.

The full source code for the demo is available here. For more information on Apache Pivot, visit http://incubator.apache.org/pivot.

application Data (computing)

Opinions expressed by DZone contributors are their own.

Related

  • AI: Do You Trust It?
  • Spring Boot Application With Spring REST and Spring Data MongoDB
  • Accelerate Innovation by Shifting Left FinOps: Part 4
  • Building A Simple AI Application in 2023 for Fun and Profit

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!