Processing XML with JSPs and XML Beans
Many XML developers today use Oracle JDeveloper as an integrated development environment (IDE) to speed their development cycle and take advantage of the component approach in building applications based on numerous prebuilt components. The JavaBeans component model is fully supported in Oracle JDeveloper. In JDeveloper, you can build JavaBeans that can later be reused in other projects, or install and use JavaBeans that are built by Oracle or third-party vendors.You can install the beans in JDeveloper on the JDeveloper Tools palette and later customize and include them in your application. Once the beans are installed, you can use drag-and-drop to add beans from the Tools palette to your application. When you add a bean to your application design surface, JDeveloper automatically generates the code needed to instantiate and customize the bean. This usually includes creating an instance of the bean, setting the bean properties to customize the bean, and adding action listeners to the bean to enable the application to handle the events generated by the bean. Because this technology is so powerful and easy to use, it is natural to encapsulate key XML functionality into JavaBeans.The Oracle XML JavaBeans are a set of XML components for Java applications or applets that make adding XML support to an application easy. These Java components can be integrated into Oracle JDeveloper to enable developers to create and deploy XML-based database applications quickly. The following beans are provided:DOMBuilder bean
XSLTransformer bean
DBAccess bean
XMLDBAccess bean
XMLDiff bean
XMLCompress bean
XSDValidator bean
If you install these beans into your JDeveloper environment, as described in Chapter 13, you benefit by the automatic code generation that JDeveloper performs when you include the beans in your application. If you do not use JDeveloper and work in a command-line JDK environment, you can also use the beans to visualize and transform XML. In this case, you simply use the beans as you would any other classes. The examples in this chapter were developed using JDeveloper.
DOMBuilder Bean
The DOMBuilder bean encapsulates the Java XML Parser with a bean interface and extends its functionality to permit asynchronous parsing. By registering a listener, Java applications can parse large or successive documents by having the control return immediately to the caller. The following sample code shows a program that takes a list of XML files as parameters and parses all of these files concurrently:
package sample;
import java.awt.event.*;
import oracle.xml.async.*;
import oracle.xml.parser.v2.*;
import org.w3c.dom.*;
import java.net.*;
import java.io.*;
public class MParse extends Object
implements DOMBuilderListener, DOMBuilderErrorListener{
int numArgs,i;
String Args[];
DOMBuilder tParser;
public MParse(String[] args) {
Args=args;
}
public void parse() {
for (i=0;i<Args.length;i++) {
// new instance of the asynchronous parser
tParser=new DOMBuilder(i);
// add this Listener object to be notified when parsing is complete
tParser.addDOMBuilderListener(this);
// or when an error occurs
tParser.addDOMBuilderErrorListener(this);
System.out.println("Start parsing "+Args[i]);
try {
tParser.parse(new URL("file:"+(String)Args[i]));
} catch (Exception e) {
System.out.println(e.toString());
}
}
System.out.println("Multiple files parsed in background threads");
}
public static void main(String[] args) {
MParse mParse = new MParse(args);
mParse.parse();
}
// Implementing DOMBuilderListener Interface
// Method called by DOMBuilder when document parsing starts
public void domBuilderStarted(DOMBuilderEvent p0) {
}
// Method called by DOMBuilder when document parsing returns an error.
public void domBuilderError(DOMBuilderEvent p0) {
}
// Method called by DOMBuilder when document parsing is completed.
public void domBuilderOver(DOMBuilderEvent p0) {
DOMBuilder parser;
XMLDocument xmlDoc;
int id;
// Get a reference to the parser instance that finished parsing.
parser=(DOMBuilder)p0.getSource();
// Get the parser id to identify the file being parsed
id=parser.getId();
System.out.println("Parse completed for file "+Args[id]);
// get the dom tree
xmlDoc=parser.getDocument();
// You can add custom code here to work with the parsed document.
}
// Implementing DOMBuilderErrorListener Interface
// This method is called when parsing error occurs.
public void domBuilderErrorCalled(DOMBuilderErrorEvent p0) {
int id=((DOMBuilder)p0.getSource()).getId();
System.out.println("Parse error for "+Args[id]+": "+
p0.getException().getMessage());
}
}
If you run this program using the following command, you get the output displayed in Figure 7-1.
java Sample.mParse booklist1.xml booklist2.xml booklist3.xml
Figure 7-1: Example output in JDeveloper that uses the DOMBuilder bean
If you have to parse a large number of files, the DOMBuilder bean can deliver significant time savings. Depending on the system and the number of concurrent threads (instances of DOMBuilder), we have achieved up to 40 percent faster times compared to parsing the files one after another.The asynchronous parsing in a background thread implemented in DOMBuilder can also be used in interactive visual applications. If the application parses a large file using the normal parser, the user interface will freeze until the document is parsed completely. This can easily be avoided if the DOMBuilder bean is used instead. After calling the parse method of DOMBuilder, the application receives the control back immediately. The application can then display a window with the message “Parsing, please wait.” The window can also show a Cancel button, so that the user can abort the operation if they decide to do so. If no user action is taken, the program resumes when domBuilderOver() is called by the DOMBuilder bean upon completion of the parsing task in the background.
XSLTransformer Bean
The XSLTransformer bean encapsulates the Java XML Parser’s XSLT processing engine with a bean interface and extends its functionality to permit asynchronous transformation. By registering a listener, Java applications can transform large or successive documents by having the control returned immediately to the caller. Because the XSL transformations are time-consuming, you may consider using this asynchronous interface for XSL transformations. The XSLTransformer bean can benefit applications that transform large numbers of files by transforming multiple files concurrently. It also can be used for visual applications to achieve responsive user interfaces. The considerations here are the same as with the DOMBuilder bean. From a programming standpoint, the preceding sample demonstrates the general approach that can also be applied to the XSLTransformer bean. The main point is that by implementing the XSLTransformerListener interface, the calling application is notified when the transformation is over. Therefore, the calling application can do something else between requesting the transformation and getting the result.
DBAccess Bean
The DBAccess bean maintains CLOB tables that are used to store XML documents. This functionality is quite useful when you are storing a wide range of XML documents and you are most interested in storing and retrieving them quickly without performing any XML processing within the database. This type of storage is also important when you wish to preserve any entities within the XML document instead of having them expanded, as would occur with an XMLType CLOB.
The database access is through JDBC to the CLOB tables that may store the XML and XSL files, or even files resulting from applying the XSL stylesheet to the XML file. The DBAccess bean provides the following functionality:
Creates and deletes CLOB tables.
Lists a CLOB table’s contents.
Adds, replaces, or deletes XML documents in the CLOB table.
The following code fragments demonstrate this functionality. These fragments assume the appropriate strings are passed in on the command line or through prompts.
// Create a new XML CLOB Table
void createXMLCLOBTable(String tableName) {
log.println("\nDemo for createXMLTables():");
try {
db.createXMLCLOBTable(con, tableName);
} catch (Exception ex) {
log.println("Error creating XML CLOB table: " + ex.getMessage());
}
log.println("Table +'" + tableName + "' successfully created.");
return;
}
// Replace XML Data in CLOB Table
void replaceXMLCLOBData( String tablename, String xmlname,
String filename) {
log.println("\nDemo for replaceXMLCLOBData() (similar to insert):");
String xmldata = loadFile(filename);
try {
db.replaceXMLCLOBData(con, tablename, xmlname, xmldata);
} catch (Exception ex) {
log.println("Error inserting into XML CLOB table: " +
ex.getMessage());
}
log.println("XML Data from +'" + filename + "' successfully
replaced in table '" + tablename + "'.");
return;
}
// Retrieve XML Data from CLOB Table
void getXMLCLOBData(String tablename, String xmlname) {
log.println("\nDemo for getXMLCLOBData():");
String xmlText=null;
try {
xmlText=db.getXMLCLOBData(con, tablename, xmlname);
} catch (Exception ex) {
log.println("Error getting XML data: " + ex.getMessage());
return;
}
log.println("XML CLOB data fetched: ");
log.println(xmlText);
return;
}
// List all XML CLOB Tables
void listXMLCLOBTables() {
// list all recognized XML CLOB tables
log.println("\nDemo for listXMLCLOBTables():");
String tableNames[];
int i;
try {
tableNames=db.getXMLCLOBTableNames(con,");
for (i=0;i<tableNames.length;i++) {
log.println("tablenamename="+tableNames[i]);
}
} catch (Exception ex) {
log.println("Error listing XML CLOB tables: "
+ ex.getMessage());
}
}
The complete DBAccess bean demo code is available as DBAccessDemo.java in the source download.
XMLDBAccess Bean
The XMLDBAccess bean is a simple extension of the DBAccess bean to support XMLType CLOBs that were introduced in Oracle9i. This bean has different packaging because it additionally depends upon the xdb.jar file that provides Java access to the XMLType. While the functionality includes all the equivalent methods to the DBAccess bean, it adds one useful function based upon the fact that the document is parsed on insert as XML. The getXMLXPathTextData() method retrieves the value of an XPath expression from the XML document stored in the table. The following code fragment illustrates this:
void getXMLTypeData(String tablename, String xmlname) {
log.println("\nDemo for getXMLTypeData():");
String xmlText=null;
try {
xmlText=db.getXMLTypeData(con, tablename, xmlname);
} catch (Exception ex) {
log.println("Error getting XMLType data: " + ex.getMessage());
return;
}
log.println("XMLType data fetched: ");
log.println(xmlText);
return;
}
On the surface it would appear that you should always use the XMLDBAccess bean over the DBAccess bean because of this increased functionality. Note, however, that since the XML documents are parsed, any referenced DTDs will need to be fetched and entities replaced. Thus, the document stored will not be identical to its inserted state when it is retrieved.
XMLDiff Bean
At some time, you may be faced with the task of determining whether two XML documents are the same. In practice this is usually not a simple character-for-character comparison, because for many XML documents, white space and linefeeds are insignificant. Instead, what is important is to determine how the structure and text content differ. The XMLDiff bean was created for just this purpose. It does a DOM tree comparison of file A to file B while producing an XSL stylesheet to convert A into B. There is additional visual functionality available that uses JPanel to visually display the differences detected.This bean has many uses and, since it uses the standards-based XSLT language to indicate the differences, it is an excellent component for pipeline applications. One particular application would be to keep track of document revisions. As each XML document is submitted, it can be compared to its previous version, and the resulting XSLT stylesheet can be stored. Another application would be to use it to detect whether material differences have occurred between XML files, such as XML schemas. You will be building such an application using the XMLDiff bean in Chapter 17.
XMLCompress Bean
One of the disadvantages of XML is the fact that whenever it is received in serialized form, it needs to be parsed in order for any operations to be performed. There is the additional disadvantage of the growth in size caused by the tag encapsulation of the data. We have seen real production schemas where the instance XML documents were 10 to 100 times the size of the data they contained! The XMLCompress bean provides an efficient solution to these situations. It encapsulates the XMLCompress serialization method that creates a binary format from the DOM that tokenizes the XML tags, resulting in a more compact XML stream. Additionally, Oracle XML processors such as XSL and XSD can read from the compressed stream directly to re-create the DOM without reparsing the document.To maximize the flexibility of the bean, both DOMCompress() and SAXCompress() are provided, which support a full range of input sources, including files, strings, and streams. Additionally, the DOM method supports the XMLType, CLOB, and BLOB Oracle SQL data- types. It should be noted that there is only a complementary DOMExpand() method, as applications that don’t need the DOM can work exclusively with SAX input and output events, therefore making a SAXExpand() function unnecessary.
XSDValidator Bean
Rounding out the set of XML beans is the XSDValidator bean. While, like the other beans, it encapsulates the XSDValidator class in the XML parser, it has one significant feature that makes it the choice for visual or editor applications. The XSDValidator bean includes a getStackList() method that returns the list of DOM tree paths that lead to the invalid node if a validation error is returned. To use this function, you need to use the java.util.vector and java.util.stack classes, which are populated with the path list as follows:
static void printError(XSDValidator xsdval) {
// Initialize list of paths
Vector vectPath = xsdval.getStackList();
// Register error handler
DocErrorHandler errHndl =
DocErrorHandler)xsdval.getError().getErrorHandler();
// Initialize list of errors
Vector errlist = errHndl.getErrorList();
if (vectPath.isEmpty()) {
System.out.println("Schema validation successful! No errors.");
}
if (!(vectPath.isEmpty())) {
// Initialize Stack for nodes
Stack tempStack ;
XMLNode xnode;
Enumeration enum1 = vectPath.elements();
Enumeration enum2 = errlist.elements();
// Print paths and corresponding nodes that had errors
while (enum1.hasMoreElements() && enum2.hasMoreElements()) {
System.out.println(enum2.nextElement());
tempStack = (Stack)enum1.nextElement();
while (!(tempStack.empty())) {
xnode = (XMLNode)((tempStack).pop());
System.out.print(xnode.getNodeName());
if (!(tempStack.empty()))
System.out.print("->");
}
System.out.println();
}
}
}
Using XML Beans in JSPs
You can use the Oracle XML beans in your JSP pages just like you would any other bean because they all conform to the Sun JavaBeans specification and include the requisite BeanInfo class that extends java.beans.SimpleBeanInfo. By using the <jsp:usebean> tags, you can embed your Java program code easily into an HTML page as follows:
<%@ page language="java"
contentType="text/xml; charset=UTF-8" %>
<jsp:useBean class="XMLDiff" id="xml_diff"
scope="request" >
<%
<jsp:setProperty name="booklist" property="file" value="booklist.xml"/>
// Your Java Code
%>
Note that you can set properties for the execution of the class using one or more <jsp:setproperty> tags.