Generating RSS in Java Web Frameworks 1
Sportsvite, a web start-up focused on 1) connecting recreational sports enthusiasts and 2) facilitating communication and scheduling of sports activities (e.g team and league games), is expanding the role of RSS across the site. The most recent addition is a feed of the Sportsvite classifieds section. For example, if I wanted a list of all of the soccer teams looking for more players in my area by zip code, the relevant url would be: http://www.sportsvite.com/xml/rss/listings?type=1&zip=20009&sportId=14
The task of generating and serving up RSS first involves initiating the appropriate query to the persistence layer from a given HTTP GET request. The query string parameters in the example above are defining the "type" of classified listing (teams looking for players), the zip code, and the associated sport identifier with the team. Sportsvite is built with the popular Java framework Struts and simply requires an Action class mapped to the endpoint url.
Hibernate is the object-relational mapping and persistence layer of choice, so the query for soccer teams looking for players in northwest Washington DC will return a set of POJOs derived from the data model for Sportsvite classifieds. For each object in the result set list, an instance of a class representing an individual RSS "item" is instantiated. This RSS object is then populated with "get" method calls on the object from the result set list. The RSS class is very basic and defined as:
public class RSS {
public RSS() {}
private String title;
private String link;
private String guid;
private String pubDate;
private String description;
// (getter and setter methods...)
As an example, populating the pubDate involves creating the RFC822 date format as per the RSS specification.
Using java.text.SimpleDateFormat, an RFC822 date string can be formatted with:
SimpleDateFormat RFC822DATEFORMAT = new SimpleDateFormat
("EEE', 'dd' 'MMM' 'yyyy' 'HH:mm:ss' 'Z",Locale.US);
The RSS object would then be populated with:
Date createdOn = (Date)listing.getCreatedOn(); rssItem.setPubDate(RFC822DATEFORMAT.format(createdOn));...where 'listing' is the name of the result set object and 'rssItem' is the instance of the RSS class.
As RSS objects are created and populated, they are collected in a list. The next step is to convert this list to XML using the ever powerful Castor marshalling tool. Castorization will produce an XML representation of the list data structure containing the RSS objects, which will then be transformed to the actual RSS feed. Here is how Castor is called:
private Document marshallRSSXML(ArrayList RSSList) {
Document doc = buildDocument();
try {
Marshaller.marshal(RSSList, (Node)doc);
} catch (MarshalException e) {
e.printStackTrace();
} catch (ValidationException e) {
e.printStackTrace();
}
return doc;
}
The Document object is of type org.w3c.dom, and the 'buildDocument' method simply creates an empty Document (using javax.xml.parsers.DocumentBuilderFactory)
to accept the result of the 'marshal' call.
The last step involves passing this XML Document in memory to a separate servlet of the web application that functions as an XSLT serialization service. In this case, it is serialization in the sense of sending the output of the XSLT engine over HTTP to the web client. The XSLT servlet is very straightforward and the key lines of code are below:
DOMSource source = new DOMSource(doc);
transformer.transform( source,
new StreamResult(response.getOutputStream()));
The javax.xml.transform.dom.DOMSource instance is created using the generated Document object available in memory
(stored in the session by the forwarding Action class).
The javax.xml.transform.Transformer instance is grabbed from a pre-compiled cache of XSLT modules, and using the
'getOutputStream' method on the javax.servlet.http.HttpServletResponse of the main 'service' method of the
servlet class allows us to send the transformation output to the requesting web client.
The XSLT is not even worth including as it just matches on '/array-list' (the root of the Castor generated XML document), creates the top level RSS 'channel' elements from passed in parameters, and applies-templates on <RSS> elements to create <item> elements.
XML Modeling for an ESB - Part 2 3
In XML Modeling for an ESB - Part 1, a simple and modular approach to building a canonical XML vocabulary was described. Core schemas represent a data model across an organization or enterprise without worrying about specific systems or applications; this is essentially a data dictionary encoded in XML Schema, split up by logical and/or functional area. For example, a core schema in the city government case study I will be using is Property.xsd, which captures data items across systems that can be associated with the logical or functional area of "property" (as in physical buildings and houses that are owned by someone or some organization).
Derived from the core are the context schemas which are directly related to specific applications or systems (to be integrated as consumer and/or producer services within the enterprise service bus). The context schemas that define request and response instance documents for ESB clients are referred to as external schemas, and these would be referenced by WSDL (for fans of SOAP and related technologies). Internal schemas define the XML message documents that are pulled or pushed among specific systems on the bus itself. One simple example of this is to consider what represents a single record of interest from a producer system. This internal schema references a core schema to grab elements under the Permit.xsd core model, and in turn is included by its corresponding external schema to define a response document.
By the way, the basic concept of core vs. context is a document engineering fundamental - more information available at Doc or Die.
Despite not being convinced of the practicality of SOAP anymore, SOAP over HTTP and WSDL are used in my current ESB architecture and development project. Reasons for this include the specific ESB product being used does not allow for simple HTTP GET requests (!), the product seems to be geared towards SOAP client calls, and using existing tools such as the Axis WSDL2Java facilitate a direct reliance on the schemas to interact with the ESB.
This excellent write-up by Dennis Sosnoski concerning WSDL2Java, Castor, and Axis was the basis for how I developed client interfaces to the bus directly from the schemas.
The work-flow involved in building Java, SOAP based web service clients to interact with ESB exposed services consists of the following: ESB internal development, core schema modifications, external and internal context schema authoring, WSDL authoring, and client code generation. To avoid numerous schema inclusions in WSDL, a wrapper schema was created to allow the WSDL to include a single document.
Here is the full set of relevant files; (the Ant script for the client stub generation is heavily based on available code from the previously mentioned article at sosnoski.com).
WSDL
External Context Schema wrapper (all schemas can be found from here)
build.xml
ERD
XML Modeling for an ESB - Part 1
Enterprise Service Bus (ESB) technology has arguably become the latest and greatest approach to enterprise integration and SOA. An ESB provides a standards based solution for building a service oriented platform. Systems and applications targeted for integration (either as consumers, producers, or both) only need to worry about "getting on the bus".
David Chappell's book stresses the importance of a canonical XML format. An ESB domain, or instance of a bus implementation within a specific environment/organization, must speak a common language between all systems on the bus. This common language can be some existing XML vocabulary or standard, or modeled from scratch using sound document engineering (this is the approach I took for various reasons).
One of the most prevalent and common problems that large organizations could face is data redundancy or inefficient data sharing. New systems and applications can become information silos over time; the quickest and cheapest solution often forgoes proper research and analysis to avoid creating redundant data.
The canonical XML format or vocabulary defining message content within an ESB IS the solution to the simple yet potentially severe data sharing problem. The vocabulary is a representation of the universal set of data items across the enterprise, agnostic of any specific system or application (i.e. the XML encoded model need not worry from which applications the elements were pulled). Commonalities across systems must be identified and defined only once, or else data redundancy or ambiguity could be exacerbated by the very solution trying to solve it.
This application agnostic, lowest level XML model is referred to as the core. The core model documents (I use XML Schema) should be 1) in the same namespace and 2) split up by functional or business area. Number one also implies to in fact use a namespace for your ESB domain's XML vocabulary. Why? Because of the most basic reason namespaces exist - to avoid element collision (if and when inter-domain ESB integration occurs) and to associate some sort of an identity to the vocabulary. Number two just avoids creating an unwieldy and huge document - simple modularization. For the set of common data items that span functional areas (and thus your set of core schemas), a separate document should be created. An example of a data item that would be placed in this common schema are globally unique identifiers shared by two or more applications.
The next level of ESB vocabulary modeling above the core is the context in which elements from the core are referenced to interface with a specific system or application. The context can be further segmented into external and internal documents. External refers to ESB request & response message documents (and could be included in WSDL). Internal directly corresponds to a specific system or application's XML encoded data flowing through the bus.
The next write up on this will include a specific, real world example on how this approach was actually implemented.