Java and XML: Web Publishing Frameworks
Pages: 1, 2, 3, 4, 5, 6, 7, 8
XSP
XSP stands for Extensible Server Pages, and is perhaps the most important development coming out of the Cocoon project. Certainly you, Constant Reader, are familiar with Java Server Pages ( JSP). JSP (in a nutshell) allow tags and inline Java code to be inserted into an otherwise normal HTML page, and then when the JSP page is requested, the resulting code is executed and the results are inserted right into the output HTML.[4] This has taken the Java and ASP worlds by storm, ostensibly simplifying server-side Java programming and allowing a separation of output and logic. However, there are still some significant problems. First, JSP does not really provide a separation of content and presentation. This is the same problem we have been talking about time and time again; changes to a banner, the color of a font, and text sizes require the JSP (with the inline Java and JavaBean references) to be modified. It also mingles content (pure data) with presentation in the same way static HTML does. Second, there is no ability to transform the JSP into any other format, or use it across applications, because the JSP specification is designed primarily for delivery of output.
XSP remedies both these problems. First, XSP is, at its heart, simply XML. Take a look at the sample XSP page in Example 9-7.
Example 9-7: Sample XSP Page
<?xml version="1.0"?><?cocoon-process type="xsp"?><?cocoon-process type="xslt"?><?xml-stylesheet href="myStylesheet.xsl" type="text/xsl"?><xsp:page language="java"xmlns:xsp="http://www.apache.org/1999/XSP/Core"><xsp:logic>private static int numHits = 0;private synchronized int getNumHits( ) {return ++numHits;}</xsp:logic><page><title>Hit Counter</title><p>I've been requested <xsp:expr>getNumHits( )</xsp:expr> times.</p></page></xsp:page>
All XML conventions are followed; for now, think of the xsp:logic element contents as "off-limits" to the XML
parser; we'll discuss that later. Other than that, the entire document is XML
with some new elements. In fact, it references an XSL stylesheet that has
nothing remarkable about it at all, as seen in Example
9-8.
Example 9-8: XSL Stylesheet for Example 9-7
<?xml version="1.0"?><xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:template match="page"><xsl:processing-instruction name="cocoon-format">type="text/html"</xsl:processing-instruction><html><head><title><xsl:value-of select="title"/></title></head><body><xsl:apply-templates select="*[not(self::title)]" /></body></html></xsl:template><xsl:template match="p"><p align="center"><xsl:apply-templates /></p></xsl:template></xsl:stylesheet>
Thus, XSP easily handles the first major problem of JSP: it allows the separation of content from presentation. This separation allows developers to handle content generation (as the XSP page can be generated from a servlet or other Java code as well as being static), while XML and XSL authors can handle presentation and styling through modification of the XSL stylesheet applied to the XSP page. Just as easily, XSP solves the other significant deficiency of JSP: because XSP processing occurs before any stylesheets are applied, the resultant XML document can be transformed into any other format. This maintains all the advantages of XML, as the XSP page can be transferred between applications as well as being used just for presentation.
Creating an XSP Page
Now that you have had a taste of XSP, let's build our own XSP page. For this example, let's continue looking at the XML documents we have already created. We revisit the XML document we constructed earlier. This document represents a portion of the first chapter of this book, and was transformed into a PDF document. Instead of simply using this document for display, let's assume that the author (me!) wants to let his editor view the document as it is being written. However, in addition to the text of the book, the editor should be able to see comments from the author that the public should not see: for example, questions about style and formatting. First, let's add the comment to the chapterOne.xml file we built earlier:
...<contents><chapter id="chapterOne"><title>Chapter 1: Introduction</title><paragraph>XML. These three letters have brought shivers toalmost every developer in the world today at some point in thelast two years. While those shivers were often fear at anotheracronym to memorize, excitement at the promise of a new technology,or annoyance at another source of confusion for today'sdeveloper, they were shivers all the same. Surprisingly, almost everytype of response was well merited with regard to XML. It is anotheracronym to memorize, and in fact brings with it a dizzying array ofcompanions: XSL, XSLT, PI, DTD, XHTML, and more. It also brings withit a huge promise-what Java did for portability of code, XML claimsto do for portability of data. Sun has even been touting therather ambitious slogan"Java + XML = Portable Code + PortableData" in recent months. And yes, XML does bring with it asignificant amount of confusion. We will seek to unravel anddemystify XML, without being so abstract and general as to beuseless, and without diving in so deeply that this becomes justanother droll specification to wade through. Thisis a book for you, the Java developer, who wants to understand thehype and use the tools that XML brings to the table.</paragraph><authorComment>Is the formatting of this first paragraph OK? Iwonder if we should break this into two separate paragraphs. Letme know what you think, Mike.</authorComment><paragraph>Today's web application now faces a wealth of problemsthat were not even considered ten years ago. Systems that aredistributed across thousands of miles must perform quickly andflawlessly. Data from heterogeneous systems, databases, directoryservices, and applications must be transferred without a singledecimal place being lost. Applications must be able to communicatenot only with other business components, but other business systemsaltogether, often across companies as well as technologies. Clientsare no longer limited to thick clients, but can be web browsers thatsupport HTML, mobile phones that support Wireless ApplicationProtocol (WAP), or handheld organizers with entirely different markuplanguages altogether. Data, and the transformation of that data, hasbecome the crucial centerpiece of every application being developedtoday.</paragraph></chapter></contents></book>
With this comment now in our XML document, let's add a corresponding entry into our XSL stylesheet, JavaXML.fo.xsl:
<xsl:template match="paragraph"><fo:block font-size="12pt" space-before.optimum="12pt"text-align="justified"><xsl:apply-templates/></fo:block></xsl:template><xsl:template match="authorComment"><fo:block font-size="10pt" font-style="italic" color="blue"space-before.optimum="12pt"text-align="justified"><xsl:apply-templates/></fo:block></xsl:template>
With this new entry, the comments will appear slightly smaller
than the rest of the text, italicized, and in blue. Now let's turn our XML
document into an XSP page (as in Example
9-9) by adding the needed processing instructions for Cocoon, and
surrounding the elements within a new root element, xsp:page.
Example 9-9: XSP Version of Example 9-1
<?xml version="1.0"?><?cocoon-process type="xsp"?><?cocoon-process type="xslt"?><?xml-stylesheet href="XSL/JavaXML.fo.xsl" type="text/xsl"?><xsp:pagelanguage="java"xmlns:xsp="http://www.apache.org/1999/XSP/Core"><book><cover><title>Java and XML</title><author>Brett McLaughlin</author></cover><!-- Content of Chapter --></book></xsp:page>
Before adding XSP logic to determine whether or not to show the comment, let's build a simple HTML page letting the viewer select whether he or she is the book's editor. In a real application, this could be a page that handles authentication and determines a user's role; for our example, it lets the user select if they are the author, the editor, or just a curious reader, and enter a password for verification. An HTML page that does this is shown in Example 9-10. You can save this file as entry.html in your web server's document root.
Example 9-10: HTML Frontend for User to Select a "Role"
<html><head><title>Welcome to the Java and XML Book in Progress</title></head><body><h1 align="center"><i>Java and XML</i> Book in Progress</h2><center><form action="/chapterOne.xml" method="POST">Select your role:<select name="userRole"><option value="author">I'm the Author</option><option value="editor">I'm the Editor</option><option value="reader">I'm a Reader</option></select><br />Enter your password:<input type="password" name="password" size="8" /><br /><br /><input type="submit" value="Take me to the Book!" /></form></center></body></html>
Also notice that we submit the HTML form directly to our XSP
page. In this example, our XSP acts similarly to a servlet. We want it to read
the request parameters, determine what user role was selected, authenticate
that role using the password supplied, and finally determine whether we should
show the comment. To begin, let's define aboolean
variable; this variable will hold the result of comparing the request
parameters to see if the user is an author or editor and supplied a correct
password. We then check the value of that variable, and if it is true, display the authorComment element:
<xsp:pagelanguage="java"xmlns:xsp="http://www.apache.org/1999/XSP/Core"><book><cover><title>Java and XML</title><author>Brett McLaughlin</author></cover>...is a book for you, the Java developer, who wants to understand thehype and use the tools that XML brings to the table.</paragraph><xsp:logic>boolean authorOrEditor = false;// Perform logic to see if user is an author or editorif (authorOrEditor) {<xsp:content><authorComment>Is the formatting of this first paragraph OK? Iwonder if we should break this into two separate paragraphs. Letme know what you think, Mike.</authorComment></xsp:content>}</xsp:logic><paragraph>Today's web application now faces a wealth of problemsthat were not even considered ten years ago. Systems that are...
This shouldn't look too odd to you; other than the XSP-specific
tags, we're just defining a variable and checking its value. If the variable
evaluates totrue, the authorComment element is added to the XSP page's output;
otherwise, the element is not included in the output. One interesting thing to
note is that we surround the actual XML document output within the xsp:logic block with an xsp:content element (which in turn is within the outer
xsp:page element). This ensures that the XSP
processor does not try to interpret any elements or text within the block as
XSP structures. This again is an improvement to JSP; the same code in JSP
might look like this:
<%if (authorOrEditor) {%><authorComment> Is the formatting of this first paragraph OK? Iwonder if we should break this into two separate paragraphs. Letme know what you think, Mike.</authorComment><%}%>
This is not very structured, as the JSP block ends before the
authorComment element begins; then a new block is
appended after the element, which closes the brackets opened in the first JSP
block. It is very easy to mismatch coding structures or forget to add matching
JSP blocks; the XSP paradigm forces every open element to be closed (standard
XML well-formedness) and one block of code is matched with one element.
With our logical structures in place, we just need to interpret
the request parameters. We use the built-in XSP variable request, which mimics the Servlet HttpServletRequest object. The following code additions
read the value of theuserRole and password request parameters (if they exist); the value is
then compared with the roles that can see the comments (author and editor). If
a match occurs, the password is checked as well. If the password matches the
key for the supplied role, theboolean variable is
set totrue, and the authorComments element is part of the XML output:
<xsp:logic>boolean authorOrEditor = false;// Perform logic to see if user is an author or editor<![CDATA[String[] roleValues = request.getParameterValues("userRole");String[] passwordValues = request.getParameterValues("password");if ((roleValues != null) && (passwordValues != null)) {String userRole = roleValues[0];String password = passwordValues[0];if (userRole.equals("author") && password.equals("brett")) {authorOrEditor = true;} elseif (userRole.equals("editor") && password.equals("mike")) {authorOrEditor = true;}]]>}if (authorOrEditor) {<xsp:content><authorComment>Is the formatting of this first paragraph OK? Iwonder if we should break this into two separate paragraphs. Letme know what you think, Mike.</authorComment></xsp:content>}</xsp:logic>
Notice that we enclose a good bit of this logic within a CDATA tag. Remember that XSP is still evaluated as XML,
and must follow the rules of an XML document; but the double quotes and
ampersands we use in the Java fragments are not allowed in XML documents;
instead of escaping these characters, and getting a very strange XSP fragment,
we use theCDATA tag so that we can write standard
Java code. Without this, we would have to code as follows:
<xsp:logic>boolean authorOrEditor = false;String[] roleValues =request.getParameterValues("userRole");String[] passwordValues =request.getParameterValues("password");if ((roleValues != null) &&(passwordValues != null)) {String userRole = roleValues[0];String password = passwordValues[0];if (userRole.equals("author") &&password.equals("brett")) {authorOrEditor = true;} elseif (userRole.equals("editor") &&password.equals("mike")) {authorOrEditor = true;}}...</xsp:logic>
You can now test out our entry page and the resultant PDF generated from the XML. You should get output similar to Figure 9-7 if you direct your web browser to http://<hostname>:<port>/entry.html.
| Figure 9-7. Entry HTML page |
|
|
Select the role of author, and use the password "brett"; otherwise use the editor role with the password "mike." Either case gives you the PDF output shown in Figure 9-8.
| Figure 9-8. Generated PDF with comments showing |
|
|
The one thing we have not yet done is isolate our logic from our content. Just as JSP allows inclusion of JavaBeans to abstract the content and presentation from the logic of an application component, XSP allows tag libraries to be created. These tag libraries can then allow XML tags to trigger the matching code within a tag library.
Using XSP Tag Libraries
In addition to showing comments based on the user, we should
indicate that the chapter is in a draft state; additionally, the current date
can be shown to indicate the date of the draft (the intention would be that
the date be frozen when the chapter is considered complete). Instead of adding
inline Java tags to load the current date, we can create a custom tag library
for this purpose. While we are at it, let's look at creating an XSP element
that takes in the chapter number and title and formats the complete title.
This function will handle the insertion of the draft date we have been talking
about. To do this, we first need to create a tag library that is available to
our XSP page. Much of the tag library is based on an XSL stylesheet. We can
start with the skeleton in Example
9-11, which passes anything it receives through as output. Save this
skeleton as JavaXML.xsp.xsl in the XSL/ subdirectory. Be sure to include the JavaXML namespace declaration, as we will use it to match
elements within that namespace used in our XSP pages.
Example 9-11: XSP Logicsheet
<?xml version="1.0"?><xsl:stylesheet version="1.0"xmlns:xsl="http://www.w3.org/1999/XSL/Transform"xmlns:xsp="http://www.apache.org/1999/XSP/Core"xmlns:JavaXML="http://www.oreilly.com/catalog/javaxml/"><xsl:template match="xsp:page"><xsp:page><xsl:copy><xsl:apply-templates select="@*"/></xsl:copy><xsl:apply-templates/></xsp:page></xsl:template><xsl:template match="@*|*|text()|processing-instruction( )"><xsl:copy><xsl:apply-templatesselect="@*|*|text()|processing-instruction( )"/></xsl:copy></xsl:template></xsl:stylesheet>
By matching thexsp:page tag, we
ensure that all elements are matched and handled within this stylesheet, or
logicsheet in XSP parlance. We can now add Java
methods for the templates within this logicsheet to call:
<xsl:template match="xsp:page"><xsp:page><xsl:copy><xsl:apply-templates select="@*"/></xsl:copy><xsp:structure><xsp:include>java.util.Date</xsp:include><xsp:include>java.text.SimpleDateFormat</xsp:include></xsp:structure><xsp:logic>private String getDraftDate( ) {return (new SimpleDateFormat("MM/dd/yyyy")).format(new Date( ));}private String getTitle(int chapterNum, String chapterTitle) {return "Chapter " + chapterNum + ": " + chapterTitle;}</xsp:logic><xsl:apply-templates/></xsp:page></xsl:template>
Several new XSP elements are introduced here. First, xsp:structure is used to surround several xsp:include statements. These work just like their Java
counterpart,include, by making the specified Java
classes available for use by their unqualified name (rather than the complete
package name). Once these are available, we define and implement two methods:
one that creates a chapter title from the chapter number and textual title,
and one that returns the current date as a formatted String. These methods are available to any elements
within this logicsheet.
We now need to define the element that specifies when an XSP
result should replace an XML element. We have already defined the JavaXML namespace in the document root element, so we use
that as the namespace for our tag library elements. Add the following
template:
<!-- Create formatted title --><xsl:template match="JavaXML:draftTitle"><xsp:expr>getTitle(<xsl:value-of select="@chapterNum" />,"<xsl:value-of select="@chapterTitle" />")</xsp:expr> - <xsp:expr>getDraftDate( )</xsp:expr></xsl:template><xsl:template match="@*|*|text()|processing-instruction( )"><xsl:copy><xsl:apply-templatesselect="@*|*|text()|processing-instruction( )"/></xsl:copy></xsl:template>
When a document with this tag library uses the element JavaXML:draftTitle, the result of the method getTitle( ) will be prepended to a dash (-), and then the returned value of the getDraftDate( ) method will be appended to that result.
TheJavaXML:draftTitle element also expects two
attributes to be declared: the chapter number and the textual title of the
chapter. We signify to the XSP processor that we are calling a defined method
by enclosing the method call within a set of <xsp:expr> tags. To
indicate that the second argument (the chapter title) is a String, we enclose it within quotes. Since the chapter
number should be treated as anint, it is left
without quotation marks.
Once you have completed the XSP logicsheet (available online at the book's web site as well), you need to make it accessible to Cocoon. This can be done one of two ways: the first is to specify the location of the file as a URI, which allows the servlet engine (and therefore Cocoon) to locate the logicsheet. For example, to add our XSP logicsheet to Cocoon's set of resources through its URI, you could add the following line to your cocoon.properties file on a Unix-based system:
processor.xsp.library.context.java =resource://org/apache/cocoon/processor/xsp/library/java/context.xslprocessor.xsp.library.cookie.java =resource://org/apache/cocoon/processor/xsp/library/java/cookie.xslprocessor.xsp.library.global.java =resource://org/apache/cocoon/processor/xsp/library/java/global.xslprocessor.xsp.library.request.java =resource://org/apache/cocoon/processor/xsp/library/java/request.xslprocessor.xsp.library.response.java =resource://org/apache/cocoon/processor/xsp/library/java/response.xslprocessor.xsp.library.session.java =resource://org/apache/cocoon/processor/xsp/library/java/session.xslprocessor.xsp.library.util.java =resource://org/apache/cocoon/processor/xsp/library/java/util.xslprocessor.xsp.library.JavaXML.java =file:///usr/local/jakarta-tomcat/webapps/ROOT/XSL/JavaXML.xsp.xsl
For Windows systems, this would be:
processor.xsp.library.context.java =resource://org/apache/cocoon/processor/xsp/library/java/context.xslprocessor.xsp.library.cookie.java =resource://org/apache/cocoon/processor/xsp/library/java/cookie.xslprocessor.xsp.library.global.java =resource://org/apache/cocoon/processor/xsp/library/java/global.xslprocessor.xsp.library.request.java =resource://org/apache/cocoon/processor/xsp/library/java/request.xslprocessor.xsp.library.response.java =resource://org/apache/cocoon/processor/xsp/library/java/response.xslprocessor.xsp.library.session.java =resource://org/apache/cocoon/processor/xsp/library/java/session.xslprocessor.xsp.library.util.java =resource://org/apache/cocoon/processor/xsp/library/java/util.xslprocessor.xsp.library.JavaXML.java =file:///C:/java/jakarta-tomcat/webapps/ROOT/XSL/JavaXML.xsp.xsl
While this is handy for testing, it is not a very good solution for uncoupling your logicsheets from the servlet engine, and also adds quite a bit of maintenance overhead when adding new logicsheets: a new line would have to be added to the Cocoon properties file for new logicsheets to be available.[5] An alternative method for loading logicsheets is to allow specification of a resource in the servlet engine's classpath. This allows all of your custom logicsheets to be added to a jar file, and that jar file to be added to the servlet engine classpath. In addition, new logicsheets can be put within the jar file, providing a central location for storing your custom XSP logicsheets. From the XSL/ subdirectory in your web server's document root, perform the following command to create a jar file that contains our logicsheet:
jar cvf logicsheets.jar JavaXML.xsp.xsl
Move the created logicsheets.jar archive into your <TOMCAT_HOME>/lib/ directory with the other Cocoon libraries. Now we need to add this library to Tomcat's class path; edit the tomcat.sh or tomcat.bat file, located in the <TOMCAT_HOME>/bin/ directory. In Unix, the edited file would look like this:
CLASSPATH=.# Cocoon classes and librariesCLASSPATH=${CLASSPATH}:${TOMCAT_HOME}/lib/xerces_1_0_3.jarCLASSPATH=${CLASSPATH}:${TOMCAT_HOME}/lib/xalan_0_20_0.jarCLASSPATH=${CLASSPATH}:${TOMCAT_HOME}/lib/fop_0_12_1.jarCLASSPATH=${CLASSPATH}:${TOMCAT_HOME}/lib/cocoon.jarCLASSPATH=${CLASSPATH}:${TOMCAT_HOME}/lib/logicsheets.jarfor i in ${TOMCAT_HOME}/lib/* ; doCLASSPATH=${CLASSPATH}:$idoneCLASSPATH=${CLASSPATH}:${JAVA_HOME}/lib/tools.jar
And on Windows:
set CLASSPATH=.set CLASSPATH=%TOMCAT_HOME%\classesrem Cocoon classes and librariesset CLASSPATH=%CLASSPATH%;%TOMCAT_HOME%\lib\xerces_1_0_3.jarset CLASSPATH=%CLASSPATH%;%TOMCAT_HOME%\lib\xalan_1_0_0.jarset CLASSPATH=%CLASSPATH%;%TOMCAT_HOME%\lib\fop_0_12_1.jarset CLASSPATH=%CLASSPATH%;%TOMCAT_HOME%\lib\cocoon.jarset CLASSPATH=%CLASSPATH%;%TOMCAT_HOME%\lib\logicsheets.jarset CLASSPATH=%CLASSPATH%;%TOMCAT_HOME%\lib\webserver.jarset CLASSPATH=%CLASSPATH%;%TOMCAT_HOME%\lib\jasper.jarset CLASSPATH=%CLASSPATH%;%TOMCAT_HOME%\lib\xml.jarset CLASSPATH=%CLASSPATH%;%TOMCAT_HOME%\lib\servlet.jarset CLASSPATH=%CLASSPATH%;%JAVA_HOME%\lib\tools.jar
With our logicsheet available, we can now let Cocoon know where
to look forJavaXML namespace references within XSP
pages. Edit the cocoon.properties file you earlier put
in the<TOMCAT_HOME>/conf/ directory. Locate the section that lists the
various Cocoon XSP resources, and add the new logicsheet reference:
processor.xsp.library.context.java =resource://org/apache/cocoon/processor/xsp/library/java/context.xslprocessor.xsp.library.cookie.java =resource://org/apache/cocoon/processor/xsp/library/java/cookie.xslprocessor.xsp.library.global.java =resource://org/apache/cocoon/processor/xsp/library/java/global.xslprocessor.xsp.library.request.java =resource://org/apache/cocoon/processor/xsp/library/java/request.xslprocessor.xsp.library.response.java =resource://org/apache/cocoon/processor/xsp/library/java/response.xslprocessor.xsp.library.session.java =resource://org/apache/cocoon/processor/xsp/library/java/session.xslprocessor.xsp.library.util.java =resource://org/apache/cocoon/processor/xsp/library/java/util.xslprocessor.xsp.logicsheet.JavaXML.java = resource://JavaXML.xsp.xsl
Because our logicsheet is not nested within any subdirectories
in the logicsheets.jar file, we simply use the name of
the logicsheet as its resource path. Finally, you will need to restart the
servlet engine. This will reload the cocoon.properties
file, and the logicsheet will be available for use. As the Cocoon engine is
used to handle requests, any XSP page that declares that it uses the JavaXML will have available to it the logicsheet
specified as theJavaXML library. So our XSP page
needs to add a namespace declaration for the JavaXML namespace:
<?xml version="1.0"?><?cocoon-process type="xsp"?><?cocoon-process type="xslt"?><?xml-stylesheet href="XSL/JavaXML.fo.xsl" type="text/xsl"?><xsp:pagelanguage="java"xmlns:xsp="http://www.apache.org/1999/XSP/Core"xmlns:JavaXML="http://www.oreilly.com/catalog/javaxml/"><book>...
With the tag library now available for use, we can finally add
in theJavaXML:draftTitle element to our XML
document, chapterOne.xml :
<contents><chapter id="chapterOne"><title><JavaXML:draftTitle chapterNum="1"chapterTitle="Introduction"/></title>
We replace the hardcoded chapter title with the element defined in our tag library. This should generate the title with the chapter number, chapter title, and the date of the draft. Accessing this new version of our XSP page results in the output shown in Figure 9-9.
| Figure 9-9. Output of XSP using a tag library |
|
|
Certainly these are simple examples, and we have only scratched the surface of what XSP allows. Even this simple example allows the title to be converted to a different form when the chapter is complete, without modifying the content or presentation of the page, but only the XSP logicsheet. In the same way, XSP allows the creation of very strict contracts separating presentation from content from application logic. Adding server-side Java components such as Enterprise JavaBeans can bring business logic into the equation. Rather than using a less flexible solution like JSP that is coupled to HTML and a presentation format, using XSP allows a looser coupling of components and thus a better solution for application development. XSP also promises to be key in the upcoming version of Cocoon, Cocoon 2.0, which we look at now.


