Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Articles
Learn about JavaFX's APIs for Reading RSS and Atom Newsfeeds   01 Dec 2009 01:03 GMT
JavaFX 1.2 introduced many interesting APIs, including APIs for reading RSS and Atom newsfeeds.
JavaFX APIs for RSS and Atom
Mon, 2009-11-30
This article introduces you to the RSS and Atom APIs. You first explore their common foundation, and then tour each API's key classes. Finally, you gain insight into how these APIs work by exploring the FeedTask class's newsfeed-polling implementation.

{cs.r.title}



JavaFX 1.2 introduced many interesting APIs, including APIs for reading RSS and Atom newsfeeds. If you haven't worked with these APIs, you'll discover that they greatly simplify the task of integrating a newsfeed reader into a JavaFX application.

This article introduces you to the RSS and Atom APIs. You first explore their common foundation, and then tour each API's key classes. Finally, you gain insight into how these APIs work by exploring the FeedTask class's newsfeed-polling implementation.

Common Foundation

The RSS and Atom APIs are offshoots of a common foundation that's rooted in the abstract javafx.async.Task class. This class makes it possible to start, stop, and track an activity (task) that runs on a background thread.

Task provides onStart and onDone variables that identify functions to be invoked at the start/end of the task, and other variables that report task progress and disposition (success or failure). This class also provides abstract start(): Void and stop(): Void functions to initiate and terminate task execution.

The abstract javafx.data.feed.FeedTask class extends Task. In addition to inheriting Task's variables, and overriding its start() and stop() functions, FeedTask provides the following functions and variables:

  • poll(): Void: Poll the newsfeed location for updated content, which is fetched, parsed, and delivered to the application.
  • update(): Void: Poll the newsfeed location. All content is fetched, parsed, and delivered to the application.
  • headers (of type javafx.io.http.HttpHeader[]) identifies a sequence of HTTP request headers that are to be sent to location each time this newfeed is polled. This variable defaults to null.
  • interval (of type javafx.lang.Duration) specifies the amount of time that must elapse before the newsfeed is once more polled for updates. You must specify a positive value for this variable, which defaults to 0.0. (I wonder if it wouldn't be better to choose a positive value, such as 60s, to be the polling default, and perhaps allow 0.0 to indicate that polling isn't desired.)
  • location (of type String) specifies the newsfeed's address. This variable defaults to the empty string ("").
  • onException (of type function(:Exception):Void) identifies a function that's invoked when an exception occurs during the current poll. This variable defaults to null.
  • onForeignEvent (of type function(:javafx.data.pull.Event):Void) identifies a function that's invoked to handle extension elements, which are newsfeed elements whose namespace URI is not Atom or RSS. For example, given an Atom newsfeed whose feed element's start tag is specified as <feed xmlns="http://www.w3.org/2005/Atom" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/">, parsing a subsequent <opensearch:totalResults>1911</opensearch:totalResults> element results in three foreign events (for the start tag, text, and end tag) because the namespace for totalResults is http://a9.com/-/spec/opensearch/1.1/ (as specified by the opensearch: prefix) instead of http://www.w3.org/2005/Atom. This variable defaults to null.

The common foundation is also rooted in the abstract javafx.data.feed.Base class, which is the base class for RSS and Atom classes that describe various newsfeed elements. RSS's RSS and Atom's Feed top-level element classes are examples of Base subclasses.

Base provides a namespaces variable (of type javafx.data.Pair[]) that contains the namespace definitions in effect for the element. The name member of each Pair specifies the namespace prefix; the value member specifies the namespace URI.

Base also provides a parent variable (of type Base) that identifies the parent (enclosing) element. For example, the parent variable of Atom's Entry element class refers to its containing Feed instance. If there's no parent (as is the case with Feed), this variable contains null.

Finally, Base provides several functions that are useful when you need to create a custom feed parser. Because this task is beyond the scope of this article, I refer you to Rakesh Menon's Custom Feed Parsers blog post for more information and an example.

RSS API Overview

The RSS (Resource Description Framework Site Summary, Really Simple Syndication, Rich Site Summary) API consists of 10 classes that are located in the javafx.data.feed.rss package. Central to this package is the RssTask class.

RSS versions supported by the API
The RSS API handles newsfeeds that conform to versions 0.91 (with non-optional item elements) through 2.0.11 (the most recent version at time of writing) of the RSS specification.

The RssTask entry-point class extends FeedTask, and provides the following variables for installing a custom factory, for reporting the newsfeed's channel element's non-item content, and for reporting the content of each of the channel element's item elements:

  • factory (of type Factory) identifies the factory that's used to create objects that represent newsfeed elements. You only need to install your own factory when creating a custom feed parser.
  • onChannel (of type function(:Channel):Void) identifies a function that's invoked to report the channel element's non-item elements -- the RSS channel element contains item and non-item elements, and is itself contained within the top-level rss element. This variable defaults to null.
  • onItem (of type function(:Item):Void) identifies a function that's invoked to report the current item element. This variable defaults to null.

The Channel class extends the abstract RSS class, which represents the top-level rss element, and which provides members for accessing the factory that's creating objects, for accessing the task that's parsing the newsfeed, and more. In turn, RSS extends Base.

Channel also provides the following variables for accessing channel-oriented (non-item-specific) content:

  • categories (of type Category[]) identifies the categories (in terms of domains and text values) to which this channel belongs.
  • copyright (of type String) specifies a copyright notice for channel content.
  • description (of type String) presents a phrase or sentence that describes this channel.
  • docs (of type String) specifies a URL that points to documentation for the format used in the RSS file. This might simply be a pointer to a Web page, and is useful for letting people, who encounter this RSS file in the future, understand the file's purpose (much like code comments).
  • generator (of type String) identifies the program that was used to generate this channel.
  • image (of type Image) identifies an image (in terms of description, height, link, title, URL, and width) that can be displayed with the channel content.
  • language (of type String) identifies the language in which the channel was written.
  • lastBuildDate (of type javafx.date.DateTime) specifies when this channel's content was last changed.
  • link (of type String) provides the URL to the Website that corresponds to this channel.
  • pubDate (of type DateTime) identifies the date when this channel was published.
  • title (of type String) provides this channel's title.
  • ttl (of type Duration) provides the number of minutes in which the news-reader can cache this channel before it must poll the newsfeed to refresh channel content.
Unsupported channel elements
For whatever reason, the RSS API doesn't support the channel element's cloud, textInput, skipHours, and skipDays elements. These elements are not represented by javafx.data.xml.QName constants in the RSS class, and they are not represented by variables in the Channel class.

As with Channel, the Item class, which describes one of the channel's item elements, extends RSS. It provides the following variables:

  • author (of type String) provides the email address of this item's author.
  • categories (of type Category[]) identifies the categories to which this item belongs.
  • comments (of type String) specifies the URL of a Web page containing comments about this item.
  • description (of type String) provides a description of this item.
  • enclosure (of type Enclosure) describes a media object (in terms of length, MIME type, and URL) that's attached to this item.
  • guid (of type Guid) specifies, for this item, a globally unique identifier (in terms of text and an indicator of whether or not this text permanently points to the full item described by this item).
  • link (of type String) provides this item's URL.
  • pubDate (of type DateTime) identifies the date when this item was published.
  • source (of type Source) identifies the originating channel (in terms of the name of the channel and an XMLization of that channel) for this item.
  • title (of type String) provides this item's title.

I've created a NetBeans RSSDemo project whose Main.fx source code demonstrates RssTask in terms of its interval, location, onStart, onChannel, onItem, onException, onForeignEvent, and onDone variables.

/*
 * Main.fx
 */

package rssdemo;

import java.lang.Exception;

import javafx.data.feed.rss.Channel;
import javafx.data.feed.rss.Item;
import javafx.data.feed.rss.RssTask;

import javafx.data.pull.Event;

def MAX_POLLS = 3;

var counter = 0;

def task:RssTask = RssTask
{
    interval: 15s

    // The following location demonstrates a basic RSS newsfeed.

    location: "http://javajeff.mb.ca/rss/javajeff.xml"

    // The following location demonstrates onException().

    // location: "http://developers.sun.com/rss/sdn_features.xml"

    // The following location demonstrates onForeignEvent().

    // location: "http://feeds.dzone.com/javalobby/frontpage?format=xml"

    // The following location demonstrates IllegalArgumentException (must use
    // AtomTask for Atom feeds).

    // location: "http://feeds.sophos.com/en/atom1_0-sophos-company-news.xml"
  
    onStart: function (): Void
    {
        println ("Task is starting");

        if (++counter > MAX_POLLS)
        {
            task.stop ();
            FX.exit ()
        }
    }

    onChannel: function (c: Channel): Void
    {
        println ("Channel: {c}")
    }

    onItem: function (i: Item): Void
    {
        println ("Item: {i}")
    }

    onException: function (e: Exception): Void
    {
        println ("Exception: {e}");
        task.stop ();
        FX.exit ()
    }

    onForeignEvent: function (e: Event): Void
    {
        println ("Event: {e}")
    }

    onDone: function (): Void
    {
        println ("Completed poll #{counter}")
    }
}
task.start ()

The source code introduces a constant that specifies the maximum number of times to poll the newsfeed, and a variable that counts the number of polls that have been made so far. The idea is to limit the number of times the newsfeed is polled so that the application won't run indefinitely.

After invoking the RssTask instance's start() function, which starts the newsfeed-polling operation, the newsfeed located at the address assigned to location is polled every 15 seconds. The onStart() callback is invoked at the start of each poll.

This callback tests to see if the counter has exceeded the maximum number of polls. If so, stop() is invoked to stop the polling, and FX.exit() is invoked to kill the background thread that's associated with the RssTask instance, allowing the application to exit.

Perhaps you're wondering why I placed if (++counter > MAX_POLLS) in onStart(), as opposed to onDone's callback. I did this because onDone() isn't always called at the end of each poll. (You'll discover why this happens later in the article.)

It's possible that an exception might be thrown as a result of the newsfeed being read or parsed. If this happens, the onException() callback invokes stop() to stop the polling task, and then invokes FX.exit() to kill the background thread and terminate the application.

This simple framework serves as a starting point for exploring the RSS API. As an exercise, expand onChannel() and onItem() to output the values of their Channel and Item arguments' various variables.

Atom API Overview

In contrast to RSS, the Atom API consists of 12 classes that are located in the javafx.data.feed.atom package. Central to this package is the AtomTask class.

Atom versions supported by the API
The Atom API handles newsfeeds that conform to version 1.0 (the most recent version at time of writing) of the Atom specification.

The AtomTask entry-point class extends FeedTask, and provides the following variables for installing a custom factory, for reporting the newsfeed's feed element's non-entry content, and for reporting the content of each of the feed element's entry elements:

  • factory (of type Factory) identifies the factory that's used to create objects that represent newsfeed elements. You only need to install your own factory when creating a custom feed parser.
  • onFeed (of type function(:Feed):Void) identifies a function that's invoked to report the feed element's non-entry elements -- the Atom feed element contains entry and non-entry elements, and is itself the top-level element. This variable defaults to null.
  • onEntry (of type function(:Entry):Void) identifies a function that's invoked to report the current entry element. This variable defaults to null.

The Feed class extends the abstract Atom class (inheriting members for accessing the newsfeed's base URI, for accessing the factory that's creating objects, and more), which extends Base.

Feed also provides the following variables for accessing feed-oriented (non-entry-specific) content:

  • authors (of type Person[]) identifies the authors (in terms of email address, name, additional person-specific text, and the Internationalized Resource Identifier (IRI) associated with the person) of this feed.
  • categories (of type Category[]) identifies the categories (in terms of a human-readable label, category name, and categorization scheme IRI) to which this feed belongs.
  • contributors (of type Person[]) identifies the persons who have contributed to this feed.
  • generator (of type Generator) identifies the program (in terms of human-readable name, program URI, and program version number) that was used to generate this feed. This information can be used to debug an Atom newsfeed.
  • icon (of type Id) identifies this feed's iconic image (in terms of a URI to the image).
  • id (of type Id) specifies a universally unique and a permanent identifier (in terms of a URI) for this feed.
  • links (of type Link[]) specifies links (in terms of href, hreflang, length, rel, title, and type XML attributes, and text associated with the link) from this feed to Web resources.
  • logo (of type Id) identifies this feed's non-iconic image.
  • rights (of type Content) specifies the rights (in terms of src, text, and type XML attributes) held in and over this feed.
  • subtitle (of type Content) provides this feed's subtitle.
  • title (of type Content) provides this feed's title.
  • updated (of type Date) specifies when this feed's content was last changed.

As with Feed, the Entry class, which describes one of the feed's entry elements, extends Atom. In addition to sharing most of the same variables as Feed, Entry provides the following unique variables:

  • content (of type Content) specifies this entry's content.
  • published (of type Date) specifies when this entry was published.
  • source (of type Feed) identifies this entry's feed source.
  • summary (of type Content) specifies a short summary, abstract, or excerpt for this entry.

I've created an AtomDemo NetBeans project for demonstrating AtomTask. This project's Main.fx source code is very similar to RSSDemo's Main.fx source code.

/*
 * Main.fx
 */

package atomdemo;

import java.lang.Exception;

import javafx.data.feed.atom.AtomTask;
import javafx.data.feed.atom.Entry;
import javafx.data.feed.atom.Feed;

import javafx.data.pull.Event;

def MAX_POLLS = 3;

var counter = 0;

def task:AtomTask = AtomTask
{
    interval: 15s

    // The following location demonstrates a basic Atom newsfeed.

    location: "http://photos.dailycamera.com/hack/feed.mg?Type=gallery&Data=9573834_9ysrR&format=atom10"
    
    // The following location demonstrates onForeignEvent().

    // location: "http://blogsearch.google.com/blogsearch/feeds?bc_lang=en&hl=en&output=atom"

    // The following location demonstrates IllegalArgumentException (must use
    // RssTask for RSS feeds).

    // location: "http://javajeff.mb.ca/rss/javajeff.xml"

    onStart: function (): Void
    {
        println ("Task is starting");

        if (++counter > MAX_POLLS)
        {
            task.stop ();
            FX.exit ()
        }
    }

    onFeed: function (f: Feed): Void
    {
        println ("Feed: {f}")
    }

    onEntry: function (e: Entry): Void
    {
        println ("Entry: {e}")
    }

    onException: function (e: Exception): Void
    {
        println ("Exception: {e}");
        task.stop ();
        FX.exit ()
    }

    onForeignEvent: function (e: Event): Void
    {
        println ("Event: {e}")
    }

    onDone: function (): Void
    {
        println ("Completed poll #{counter}")
    }
}
task.start ()

This simple framework serves as a starting point for exploring the Atom API. Consider expanding onFeed() and onEntry() to output the values of their Feed and Entry arguments' various variables.

Behind the Scenes with FeedTask

The important task of polling an RSS or Atom newsfeed occurs in FeedTask and a related class. I recently decompiled these classes to explore how newsfeeds are polled, and share my findings in this section to deepen your understanding of RssTask and AtomTask.

FeedTask creates an instance of the java.util.Timer class in its static initializer. This instance starts a background thread and works with an instance of FeedTask's nested SubscriptionTask class (a java.util.TimerTask subclass) to support newsfeed-polling.

FeedTask's overridden start() function schedules the SubscriptionTask instance for execution by invoking Timer's public void schedule(TimerTask task, long delay, long period) method with the following arguments:

  • The SubscriptionTask instance is passed to task.
  • The long integer 0L is passed to delay.
  • The value of FeedTask's interval variable is passed to period.

Approximately every period milliseconds, the SubscriptionTask instance's public void run() method is invoked. This method invokes the SubscriptionTask-specific doPoll() method with a true argument.

The doPoll() method first clears FeedTask's inherited started, stopped, failed, and done Boolean variables to false. It also nulls out the inherited causeOfFailure variable, and assigns -1 to the inherited progress and maxProgress variables.

doPoll() next instantiates the javafx.io.http.HttpRequest class, which is the vehicle used to obtain newsfeed content, and initializes the following HttpRequest variables prior to executing this task:

  • location: The value of FeedTask's location variable is assigned to this variable.
  • onStarted: A function is assigned to this variable, and is invoked when the request starts to execute. The function is responsible for invoking onStart().
  • onResponseHeaders: A function is assigned to this variable to retrieve and save the values of the HTTP ETag and Last-Modified response headers. These values are needed to ensure that only changed newsfeed content will be returned in the next poll request.
  • onToRead: A function is assigned to this variable to obtain the total number of bytes to read, which is assigned to maxProgress.
  • onRead: A function is assigned to this variable to obtain the number of bytes read so far, which is assigned to progress.
  • onInput: A function is assigned to this variable to parse the request content via an internal parse(is) method call (where is is onInput()'s java.io.InputStream argument). If parsing results in a thrown exception, the exception object is assigned to causeOfFailure, true is assigned to failed, and onException() is invoked. Finally, true is assigned to done, and onDone() is invoked. (The onInput() function isn't invoked, and hence onDone() isn't invoked, when only changed content is requested but that content isn't available.)
  • onException: A function is assigned to this variable to report a problem with the request itself (and not parsing). If the request fails, the exception object is assigned to causeOfFailure, true is assigned to failed, and onException() is invoked.

Continuing, doPoll() ensures that only updated newsfeed content is returned by setting the request's If-Modified-Since and If-None-Match headers to the previously saved Last-Modified and ETag values, respectively.

Obtaining a newsfeed's updated versus entire content
When true is passed to doPoll(), which happens when this method is called from SubscriptionTask's run() method or FeedTask's poll() function, doPoll() sets If-Modified-Since and If-None-Match so that only updated content is returned. In contrast, when you invoke FeedTask's update() function, which invokes doPoll() with a false argument, those request headers will not be set, and the entire content will be returned.

doPoll() now iterates over FeedTask's headers variable, assigning each stored HttpHeader instance to the HttpRequest instance by invoking the latter instance's setHeader() function.

Finally, doPoll() invokes the HttpRequest instance's start() function to execute this task, resulting in retrieved and parsed content. doPoll() then returns to the run() method. If it throws an exception, run() invokes onException().

A parsing tidbit
For brevity, I don't discuss parsing beyond the parse(is) method call. However, if you decide to explore the parsing implementation, here's a tidbit to save you some head-scratching: The parse(InputStream) method initializes the javafx.data.pull.PullParser instance's impl_skippedElements variable to the qualified names of Atom's summary, content, rights, title, and subtitle elements, and RSS's description, title, and copyright elements, to ensure that the parser treats any HTML or other markup that's embedded in these elements as literal text.

At some point, you'll probably invoke FeedTask's overridden stop() function. This function invokes the SubscriptionTask instance's inherited public boolean cancel() method to cancel the newsfeed-polling task (but not kill the Timer instance's background thread).

Conclusion

Enough theory! Now that you've gained knowledge of JavaFX's RSS and Atom APIs, you might want to create your own newsfeed reader. To help you with this task, I present a practical example that handles RSS and Atom newsfeeds in my forthcoming companion to this article.

Resources

Jeff Friesen is a freelance software developer and educator specializing in Java technology. Check out his site at javajeff.mb.ca.

Source: Java.net
What's New in JavaFX 1.2: Charts   30 Nov 2009 18:00 GMT
This third article in a series describes how to use the different JavaFX charts with the StockReaderFX example application.
Source: Sun
Java Mobile Podcast 91: MIDP 3.0   24 Nov 2009 23:43 GMT
Excerpts from the JavaOne 2009 MIDP 3.0 session with Angus Huang, Roger Riggs, and Paul Su.
Java Mobile Podcast 91: MIDP 3.0
Tue, 2009-11-24
Excerpts from the JavaOne 2009 MIDP 3.0 session with Angus Huang, Roger Riggs, and Paul Su.



Excerpts from the JavaOne 2009 MIDP 3.0 session with Angus Huang, Roger Riggs, and Paul Su.

Download Audio

Right-click or Control-click to download this MP3 file. You can also subscribe to the Java Mobility Podcast Feed to get the latest podcast automatically. If you use iTunes you can open iTunes and subscribe with this link: Java Mobility Podcast in iTunes.


View all Java Mobility Podcasts.

Daniel H. Steinberg runs dimsumthinking.com and is former editor-in-chief of java.net.

Source: Java.net
New in JavaFX 1.2: RSS and Storage   23 Nov 2009 18:00 GMT
This second article in a series covers the more technical features, such as RSS and Atom tasks and local storage using JavaFX's built-in storage classes.
Source: Sun
Servlet 3.0: A Sneak Preview   11 Nov 2009 01:32 GMT
Find out which features in the upcoming Servlet 3.0 specification will affect a major change in the way developers build Java web applications.
Source: DevX
Deep Dive Video: Java Warehouse and Java Store   09 Nov 2009 18:00 GMT
Join Bernard Traversat, Director of Engineering for the Java Store, as he discusses and demonstrates the latest advancement in Java technology: the consumer Java Store and the Java Warehouse for developers.
Source: Sun
Using a Service Delegate to Avoid MVC Controller Bloat   05 Nov 2009 00:10 GMT
This article describes how to maintain separation of concerns and avoid MVC controller bloat through the use of service delegates.
Avoid MVC Controller Bloat
Wed, 2009-11-04
This article describes how to maintain separation of concerns and avoid MVC controller bloat through the use of service delegates.

{cs.r.title}



One of the key concepts in the software industry is that of separation of concerns: the only way to reliably build any type of complex system is to break it down into small, simple, and focused components that each perform a specific function. Performing only a specific function makes each component easier to understand, develop, unit test, reuse, and maintain. You see strict adherence of separation of concerns within the standard three tier Java web application design that has come to dominate Java software development. It is common practice to use a web model-view-controller (MVC) framework in the presentation/web tier (such as Spring Web MVC or Struts2), a middleware framework in the middle tier (such as Spring), and an object-relational mapping (ORM) tool in the data tier (such as Hibernate or any JPA implementation).

The whole point of using frameworks and tools is to free yourself from the complex technical challenges and boiler plate code you would otherwise be forced to write on your own and allow you to be more focused and productive while developing your application. But because frameworks and tools handle the hard parts (and are also reused in a wide range of situations), they tend to be more technically complex than the application you are using them to create. The result is that frameworks and tools typically demand much more emphasis on architecture and design, and developers working on frameworks and tools tend to be much more diligent in incorporating design best practices into their software. Once again, this frees the application developer to enjoy the benefits of having to "just" design and build an application that takes maximum advantage of all of the frameworks and tools and ensure that they all integrate and interact well not only with each other but with the application's custom business logic.

And therein lies the design gap: while the creators of frameworks and tools must invest a great deal of attention and focus on design, application developers can afford to be more lax. A classic example of the design gap is what I call controller bloat. In the case of web applications, controller bloat occurs when application developers violate separation of concerns and put non-web service code directly into their web MVC framework's controllers.

When an object receives a request, the object can choose to either perform the request itself or to delegate the request to a second object which will do the work. The industry naming of this second object varies, but I refer to it as the service delegate. This term reinforces this second object's role as a delegate, and also underscores the fact that it contains service level business logic.

This article discusses using a service delegate in combination with your MVC framework for a design that avoids controller bloat. A background with the MVC pattern is assumed, and although the examples use Java and Spring Web MVC to apply the design to a web application, the concepts apply to any language as well as to any MVC framework. The terms service, service code, service layer, and service delegate are used interchangeably to refer to your custom business logic.

The problem: MVC Controller Bloat

Using Spring Web MVC as an example, let's look at a sample of a typical web MVC controller.

public class TypicalParameterizableViewController extends ParameterizableViewController
{
    private UserService userService;
    
    protected ModelAndView handleRequestInternal(HttpServletRequest request, 
                                                 HttpServletResponse response) throws Exception
    {
        Map model = new HashMap();
        
        String userId = request.getParameter("id");
        
        User user = userService.findById(userId);       
        
        user.setLastAccessTime(new Date());
        userService.persist(user);
        
        if (user.isAccountExpirationWithin90Days())
        {
            userService.sendAccountExpirationWarningEmail();
        }
        
        // Dispatch to the view
        String viewName = getViewName();
        ModelAndView modelAndView = new ModelAndView(viewName, model);
        
        return modelAndView; 
    }
}

While the above code sample is functional, it does exhibit a major design problem: service code (i.e., your business logic) is unnecessarily located within the controller itself. The majority of the code within the handleRequestInternal method has nothing to do with the web tier and therefore violates separation of concerns. This leads to a handful of cascading disadvantages. Because the code lives in a class directly depending on the web tier, it can't be easily reused within non-web-based applications. Next, just as the code is difficult to reuse outside of a web-based application, it is difficult to unit test. While there is a plethora of options such as using mock objects or sophisticated strategies such as hot deploying code to running application servers and automated remote unit tests, that fact that your service coded is coupled to your presentation tier complicates both its testing and its reuse.

All of these problems stem from the same root cause: there is no reason to have the service code located in the controller. In my opinion, a controller should be as "thin" as possible, and only contain code that directly relates to handling the incoming request, delegating all non-request/response processing to a service delegate that will generate the model objects for use in the view, and then create and return the outgoing response. Because MVC frameworks handle the first and third responsibilities for you, by relocating service code (the second responsibility) to a service delegate, controller bloat can be completely avoided. While most architects and developers agree with this design philosophy, in practice few actually incorporate it into their software.

A Solution: The ViewService

Knowing that we want our controllers to delegate processing to a service delegate, the first step in moving away from or preventing controller bloat is to create an interface to act as a behavioral contract for the interaction between the controller and the delegate.

public interface ViewService
{   
    public String REQUEST_PARAMETERS_KEY = "request-parameters";
    public String REQUEST_QUERY_STRING_KEY = "request-query-string";
    
    public void generateViewModel(Map model);
}

While it's easy to be initially underwhelmed, the ViewService's power and flexibility are derived from its simplicity. Because it does not contain any implementation details of either the presentation or service tier, it's able to act as an independent intermediary without allowing one layer to bleed into the other. As you'll see shortly, from the controller perspective, all the controller knows is that it will invoke this method with a Map as an argument, and that once the invocation is complete the results will be available in the Map. From the service perspective, all the service class knows is that this method will be invoked with a Map of input objects, that it should use these input objects to generate whatever data the implementing class is responsible for, and then return the results through the same Map. By agreeing to pass only a Map between layers, each side is fully encapsulated and has no knowledge of how the other side works.

This one, simple, single line interface leads to many tangible benefits. The use of the ViewService interface completely decouples your code from your selection of which MVC framework you use. Your MVC selection no longer needs to be a strategic or organization-wide decision because switching, or even using a separate MVC framework per application, is now trivial. Each application you create now has the freedom and flexibility to use the best framework for the given requirements and technical circumstances.

Going even further, note that a class implementing the ViewService can now be reused in any type of application: web (servlet or portal based), batch, fat client, EJB, non-EJB, and remoting applications such as web service or JMS based software are now all possibilities. This level of reuse will allow you to follow the Don't Repeat Yourself (DRY) principal, and because you are programming to interfaces, an industry best practice, your presentation and service tiers can now be built in parallel by multiple developers. Unit testing of your service code is significantly less complicated because it can be exercised through straightforward unit tests interacting directly with your service layer. This greatly simplifies testing, makes it easier to isolate issues, and saves you time.

It doesn't scream for your attention, but another subtle benefit is that this design makes it easier for new team members or less experienced developers to join and contribute to your project. For a multitude of reasons, not all developers can jump head first into an existing system and make substantial contributions right away. By having your layers and your complexity cleanly separated, you can have a particular developer work on an individual area where they are strongest or most comfortable, while they gain experience with or knowledge of a different part of the system.

With the ViewService in place, the next step is to incorporate it into your application. The following code sample shows a Spring Web MVC Controller that, through the use of a support class, delegates to an underlying ViewService implementation.

public class ViewServiceParameterizableViewController extends ParameterizableViewController
{
    private ViewService viewService;
    
    protected ModelAndView handleRequestInternal(HttpServletRequest request, 
                                                 HttpServletResponse response) throws Exception
    {
        Map model = HttpViewServiceSupport.processRequest(request, response, viewService);
        
        String viewName = getViewName();
        ModelAndView modelAndView = new ModelAndView(viewName, model);
        
        return modelAndView; 
    }
    ...
}

As with the ViewService, it's easy to be underwhelmed by the controller's simplicity. But once again, the controller's power and flexibility is derived from its simplicity. Holding true to the intent and vision of the design, the controller is as thin as practically possible. It's so thin, as you can see, that creating controllers for any other MVC framework is trivial and quick work.

Now that we've created an ultra thin controller, let's take a look at the HttpViewServiceSupport class that contains the core functionality.

public class HttpViewServiceSupport
{   
    public static Map processRequest(HttpServletRequest request, 
                                     HttpServletResponse response, 
                                     ViewService viewService)
    {
        Map model = new HashMap();
        
        buildModelFromRequest(request, model);
        
        viewService.generateViewModel(model); 

        return model;
    }
    
    public static void buildModelFromRequest(HttpServletRequest request, Map model)
    {        
        String queryString = request.getQueryString();
        model.put(ViewService.REQUEST_QUERY_STRING_KEY, queryString);
        
        processParameters(request, model);                
        processAttributes(request, model);
        processCookies(request, model);
        processHeaders(request, model);
    }

    public static void processParameters(HttpServletRequest request, Map model)
    {        
        Enumeration parameterNames = request.getParameterNames();

        if (parameterNames.hasMoreElements())
        {
            Map parameters = new HashMap();
            
            while (parameterNames.hasMoreElements())
            {
                String parameterName = (String) parameterNames.nextElement();
                String values[] = request.getParameterValues(parameterName);
                            
                parameters.put(parameterName, values);
            }
            
            model.put(ViewService.REQUEST_PARAMETERS_KEY, parameters);
        }
    }    
    ...
}

For any incoming request, processRequest()'s execution flow is composed of four steps. First, an empty Map is instantiated. As you know from earlier in the discussion, the central concept of the entire design is to pass only a Map between the various layers of the system (in our case the controller and the ViewService), and the very fact that this Map is just a plan old Java object (POJO) means that no layer is tightly coupled to any other. Second, the incoming request is transformed from it's current presentation-tier-specific format (in this case, an HttpServletRequest) into a more generic form. This is accomplished in buildModelFromRequest(), which extracts the interesting or useful information from the incoming request and places it into the previously instantiated Map. Third, the ViewService is invoked, and the underlying implementation performs its processing. Once processing is complete, the ViewService will store its results into the Map and thereby make the results available to any portion of the system from which the Map is accessible. Finally, with the ViewService's work complete, the populated Map is simply returned to the invoking method. From there, it is ultimately returned to your MVC framework and, as you would expect, the normal processing flow of your MVC framework takes place.

Having seen an ultra thin controller as well as a MVC independent support class used to process the incoming request and invoke an underlying ViewService, let's next look at an example implementation of the ViewService.

public class UserLoginService implements ViewService
{
    private UserService userService;
    
    public static final String USER_KEY = "user";
    public static final String USER_ID_KEY = "user-id";
    
    public void generateViewModel(Map model)
    {
        String userId = (String) model.get(USER_ID_KEY);
        
        User user = userService.findById(userId);       
        model.put(USER_KEY, user);
        
        user.setLastAccessTime(new Date());
        userService.persist(user);
        
        if (user.isAccountExpirationWithin90Days())
        {
            userService.sendAccountExpirationWarningEmail();
        }
        
        Map parameterMap = (Map) model.get(ViewService.REQUEST_PARAMETERS_KEY);        
        String day[] = (String[]) parameterMap.get("day");
        
        if (day != null)
        {
            model.put("day", day[0]);            
        }        
    }
    ...
}

You'll hopefully recognize most of the above code from our original and flawed example. What's new is the use of String constants as well known key names to store objects within the Map. Finally, here is a small JSP page to put it all together.

]]>

Welcome ${user.firstname}! 
Your last login was ${user.lastAccessTime}.

<c:if test="${not empty day}">
  Today is ${day}.
</c:if>

Using the same well known key names as provided by the ViewService implementation, JSTL was used to pull the model objects from the JSP context and dynamically populate the JSP page.

Conclusion

Separation of concerns is a central concept in the software industry. Controller bloat, such when a MVC controller violates separation of concerns and unnecessarily includes service code, leads to many significant problems. This article presented a reusable design in which a service delegate was used in combination with a MVC framework that allowed all of these problems to be avoided. While sample code demonstrating the design used Spring Web MVC for a Java based web application, the concepts presented universally apply to all types of applications as well as software created in any language for any platform. Because any time you emphasis good design, the end result is that your code will be easier to understand, develop, test, reuse, and maintain.

Resources

Eric Spiegelberg is a Minneapolis-based Java/EE consultant specializing in Spring, Hibernate, and web-based software development.

Source: Java.net
POJO-Based Solutions for LDAP Access: One Good, One Better   04 Nov 2009 01:32 GMT
Find out how to employ dependency injection, annotation, and aspect-oriented programming to enable POJO-based application development.
Source: DevX
"Transparent" Panel - Mixing Heavyweight and Lightweight Components   02 Nov 2009 23:56 GMT
Marina Kamahele provides instruction on overlaying lightweight Swing widgets on top of heavyweight AWT components using a TransparentPanel.
"Transparent" Panel
Mon, 2009-11-02
This Java Tech Column provides instruction on overlaying lightweight Swing widgets on top of heavyweight AWT components using a TransparentPanel.



When overlaying lightweight Swing widgets on top of heavyweight AWT components, it is convenient to group the Swing widgets on a transparent (non-opaque) JPanel so that one can use Swing layout managers and/or Swing utilities on a group of related Swing widgets. For example, all widgets added to the transparent JPanel can be made visible or invisible simultaneously with one setVisible() call on the JPanel. Unfortunately, a lightweight JPanel overlaid on top of a heavyweight AWT component cannot be transparent.

To overcome this problem, one can implement a TransparentPanel as shown below.


public class TransparentPanel extends JPanel {

    /**
     * Default constructor. Sets the layout manager to JPanel's
     * default layout manager: FlowLayout.
     */
    public TransparentPanel() {
        this(new FlowLayout());
    }

    /**
     * Construct panel with input layout manager.
     *
     * @param mgr The layout manager for the extended JPanel.
     */
    public TransparentPanel(LayoutManager mgr) {

        // set layout manager, if any
        super(mgr);

        // set opaque false so that an isOpaque() == false
        super.setOpaque(false);
   
	// set mixing cutout -- see AWTUtilitesClass below
	AWTUtilitiesClass.setMixingCutoutShape(this, 
                                               new Rectangle());
    }

    /**
     * Override setOpaque so that the user cannot change the 
     * opacity of this panel, since it is after all suppose to 
     * be transparent.
     *
     * @param isOpaque Is this panel opaque?
     */
    @Override
    public void setOpaque(boolean isOpaque) {
        // do not allow this to become opaque because it is
        // transparent after all
    }

}

Note that the constructor of TransparentPanel invokes AWTUtilitiesClass.setMixingCutoutShape(). Invoking AWTUtilitiesClass.setMixingCutoutShape() is necessary to avoid non-rendering of our TransparentPanel which would result in a solid gray (and not transparent!) TransparentPanel. The AWTUtilitiesClass "mixing cutout" technique is described in Mixing Heavyweight and Lightweight Components, October 2009 by Sharon Zakhour and Anthony Petrov, whose code has been excerpted below:


public class AWTUtilitiesClass {

    private static Method mSetComponentMixing;

    static try {
        Class awtUtilitiesClass = 
              Class.forName("com.sun.awt.AWTUtilities");
        Method mSetComponentMixing = 
              awtUtilitiesClass.getMethod(
                            "setComponentMixingCutoutShape", 
                            Component.class, Shape.class);
              mSetComponentMixing.invoke(null, component, shape);
	} catch (Execption ex) {
              ex.printStackTrace();
	}

    public static void setMixingCutoutShape(Component c, Shape s) 
    {
        if (mSetComponentMixing != null) {
            try {
                mSetComponentMixing.invoke( null, c, s );
            } catch (Exception ex) {
                ex.printStackTrace();
            }
	}
    }
}

Real World Example: Mixing Swing and Java WorldWind - Software vs Hardware Acceleration

When developing Java WorldWind applications, it is typical for the developer to overlay a JPanel (which contains Java Swing GUI components) above a WorldWind Java container (which contains the rendered world). The WorldWind Java container is usually (1) a GLJPanel which is a subclass of the lightweight Swing JPanel, or (2) a GLCanvas which is a subclass of the heavyweight AWT Canvas.

If Option 1, GLJPanel, is utilized, then the Java WorldWind developer (a) maintains the ability to overlay transparent Swing components above the world (that is, the GLJPanel). However, when attempting to turn on software acceleration, the developer will often find that doing so (b) creates run time exceptions that are difficult if not impossible to overcome, and (c) the performance gained is inferior to performance gains of hardware acceleration.

If Option 2, GLCanvas, is utilized, then the Java WorldWind developer (a) loses the ability to overlay transparent Swing components above the world (that is, the GLCanvas). However (b) runtime exceptions are not experienced, and (c) the performance gained is superior to software acceleration. Additionally, the TransparentPanel introduced in this article coupled with the "mixing cutout" from A. Petrov's article restores the ability for the Java WorldWind developer to maintain inter-component transparency, thus partially alleviating the problems of (a) and more readily allowing the Java WorldWind developer to combine lightweight Swing overlays (containing "grouped" Swing widgets) with heavyweight hardware acceleration.

Notes:

During development, when using Java Swing components rendered in an overlay above a Java WorldWind layer, a predictable gray flash (flicker) was detected. To alleviate this problem, one can utilize the flag:

-Dsun.awt.noerasebackground=true

If one wishes to override the paint() method of a TransparentPanel subclass such that the subclass uses Java2D to draw a Shape onto the superclass TransparentPanel, then the "mixing cutout" described above can cause the TransparentPanel and anything drawn on it to vanish. To resolve this, instead of passing the empty Shape via new Rectangle() to AWTUtilitiesClass.setMixingCutoutShape(), one should pass the shape of the desired drawing. See Mixing Heavyweight and Lightweight Components, October 2009.

If one wishes to overlay a non-rectangular lightweight component above a heavyweight component, one must bear in mind that Java Shapes (and thus "mixing cutouts") cannot be anti-aliased, so for non-rectangular Swing components, one must smooth the non-aliased "mixing cutouts" using hues or blurring.

Resources

Marina Kamahele has a graduate degree in Computer Science from University of California. Marina is currently a Senior Software engineer at a large software and electronics company.

Source: Java.net
Tech Tip: Using CDI and Dependency Injection for Java in a JSF 2.0 Application   02 Nov 2009 17:00 GMT
Explore an application that illustrates the use of three powerful technologies in the Java EE 6 platform: Contexts and Dependency Injection, Dependency Injection For Java, and JavaServer Faces 2.0.
Source: Sun
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage




©2010 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.