WebLogics Streaming API

WebLogic s Streaming API

WebLogic's Streaming API offers a simple and intuitive way to parse and generate XML data. Compared to the SAX or DOM parsing models, it presents a fundamentally different viewpoint on an XML document. As the name suggests, parsing with the Streaming API is based around a stream. This stream is, in fact, a stream of XML events generated as the XML document is parsed. These events are similar to the events defined in the SAX API because they represent the same fundamental information about the XML data.

BEA is involved in the standardization of the Streaming API for XML (StAX), which has an API very similar to that described here. As a result, you can consider using BEA's implementation of StAX, available from the dev2dev web site.

When an XML document is parsed in SAX mode, the program registers a handler that can listen for SAX events as they occur. The SAX parser then automatically invokes the different callback methods of the event listener. In contrast, a program using the Streaming API pulls events off a stream, whereby each event represents some fundamental information about the XML being parsed. The Streaming API supports events that mark the occurrence of start and end tags, character data, whitespace characters, processing instructions, and several other document characteristics. These parser events enable you to step through the XML document, filter out certain event types, perhaps skip ahead in the document, and stop processing at any point. Parsing an XML document entails iterating over the stream of events and processing the XML data depending on the type of parse event. Thus, parsing with the Streaming API is demand-driven because you need to explicitly iterate over the stream and extract the events of interest. For this reason, it is often referred to as pull parsing.

Assume that xmlInput is a string variable holding the XML fragment shown in Example 18-5.

Example 18-5. Sample XML

 Hello <inOne> <simple/> inOne>
 <inTwo> World inTwo>

The following piece of code illustrates how you can parse the XML data using the Streaming API:

import weblogic.xml.stream.*;
// 
String xmlInput = /* as in Example 18-5 */;
// Create a stream
XMLInputStreamFactory factory = XMLInputStreamFactory.newInstance( ); 
XMLInputStream stream = factory.newInputStream(new StringReader(xmlInput));
// Iterate over the stream
while (stream.hasNext( )) 
 // Ask for the next event off the stream
 XMLEvent e = stream.next( );
 // Do something with the event
 System.out.print(e.getTypeAsString( ));
}
stream.close( ); // Always close your streams

Notice how we create the XML input stream, then iterate over the stream of events and process each event that is encountered. Here, the program simply lists the type of XML event generated during the parse. Example 18-6 lists the output generated as a result of running this program. (Note that we have indented the list for readability purposes.)

Example 18-6. Events generated while parsing the sample XML

START_DOCUMENT 
 START_ELEMENT
 CHARACTER_DATA 
 START_ELEMENT SPACE 
 START_ELEMENT
 END_ELEMENT
 SPACE 
 END_ELEMENT
 SPACE 
 START_ELEMENT
 CHARACTER_DATA 
 END_ELEMENT
 END_ELEMENT
END_DOCUMENT

You now can see the similarity between the events generated using the Streaming API and SAX events generated by a SAX parser. The Streaming API generates events that indicate the start and end of the document, the start and end of an XML element, whitespace characters, and character data used in a tag's body. Later, we shall look at all the XML event types that can be generated when parsing with the Streaming API.

The Streaming API provides a number of useful enhancements to iterating over a stream of events:

  • Instead of handling all events, you can apply a filter to the stream so that only specific event types permeate through.
  • The Streaming API also allows you to skip a number of events, or skip until a particular event occurs.

You also can use the Streaming API to generate XML data. In this case, the entire process is reversed. Instead of requesting events from the stream, you create an XML output stream and then write elements to this stream. The Streaming API provides an ElementFactory class that manufactures the XML elements that you'll need. When writing to the XML output stream, you need to construct the XML document in a serial fashion. For each element, you need to create the start tag, its attributes, the body that may include other elements, and finally the end tag.

These features make the Streaming API a very useful addition to the current arsenal of SAX and DOM parsers. Each model has its own niche the Streaming API is best suited when you need to process only a subset of the events generated during the parse. Because the Streaming API relies on WebLogic's FastParser, you cannot use it to validate an XML document. Remember, the Streaming API is proprietary and not yet supported by the JAXP interface. This means you cannot use the JAXP interface to create a streaming parser. Therefore, neither the XML Registry nor the application-scoped parser factories can impact how you use the Streaming API.

18.5.1 Creating a Stream

With the Streaming API, the parsing occurs implicitly as you iterate through the stream of events. So, you don't even explicitly create a streaming parser; instead, you create an XMLInputStream instance, which then acts as source of parse events:

XMLInputStreamFactory factory = XMLInputStreamFactory.newInstance( );
XMLInputStream stream = factory.newInputStream(someSource);

This stream can be created from a number of different sources, including the following:

java.io.File, java.io.InputStream, or java.io.Reader

The source XML data is read from a file, byte stream, or character-based reader (as we saw in the earlier example).

org.w3c.dom.Document or org.w3c.dom.Node

The source XML data is read from a DOM tree, perhaps created previously by a DOM parser.

XMLInputStream

It may seem odd that the source for a stream may be a stream itself, but as we shall see later in this chapter, the Streaming API allows you to snap off a substream to be used for a different parse. The substream can be created by calling the getSubStream( ) method on an existing stream.

Once you create a stream, you can start parsing the XML data by iterating over the stream. The XMLInputStream provides the familiar iterator pattern for retrieving the next parse event:

while(stream.hasNext( )) {
 XMLEvent event = stream.next( );
 // Do something with the event
}

18.5.2 Events

As an XML document is parsed, the XMLInputStream object generates a stream of parser events. A typical handler will determine the event's type and then process the event accordingly. As we have already seen, the Streaming API supports a number of different types of events. In fact, the Streaming API provides an interface that corresponds to each event type, and all these interfaces extend the XMLEvent interface (one way or the other). Table 18-2 provides a complete list of XMLEvent subinterfaces provided by the Streaming API.

Table 18-2. XMLEvent subinterfaces

XMLEvent subclass

Description

Constant identifier

StartDocument

Indicates the start of an XML document

START_DOCUMENT

EndDocument

Indicates the end of an XML document

END_DOCUMENT

StartElement

Indicates the start tag for an element has been encountered

START_ELEMENT

EndElement

Indicates the end tag for an element has been encountered

END_ELEMENT

CharacterData

Indicates character data from the body of an element has been encountered

CHARACTER_DATA

Space

Indicates whitespace characters have been encountered

SPACE

Comment

Indicates an XML comment has been encountered

COMMENT

ProcessingInstruction

Indicates an XML processing instruction has been encountered

PROCESSING_INSTRUCTION

StartPrefixMapping

Indicates prefix mapping has started its scope (triggered before the StartElement event)

START_PREFIX_MAPPING

EndPrefixMapping

Indicates prefix mapping has ended its scope (triggered after the EndElement event)

END_PREFIX_MAPPING

ChangePrefixMapping

Indicates transition from one prefix mapping to another

CHANGE_PREFIX_MAPPING

EntityReference

Indicates an entity reference has been encountered

ENTITY_REFERENCE

The Constant Identifier column indicates the name of an integer constant in the XMLEvent interface that identifies each event type. The getType( ) method on an XMLEvent object returns a value that matches one of these constants.

The XMLEvent interface also defines an is( ) method for each of the XML events, which allows you to identify the actual event subclass. Once you determine its type, you can cast the XMLEvent object to the correct subinterface and process the event accordingly. For example, when a new namespace prefix is introduced, the XML stream returns a StartPrefixMapping event. It provides various methods for accessing the namespace datae.g., the getNamespaceUri( ) and getPrefix( ) methods. Similarly, a StartElement instance has methods for accessing the attributes of an element:

//alternative check:
// if (event.getType( ) == XMLEvent.START_ELEMENT) { 
// ...
// }
if (event.isStartElement( )) {
 StartElement startElement = (StartElement) event;
 AttributeIterator attributes = startElement.getAttributesAndNamespaces( );
 while(attributes.hasNext( )){
 Attribute attribute = attributes.next( );
 System.out.print("Name of attr: " + attribute.getName( ).getQualifiedName( ));
 System.out.print("Value of attr: " + attribute.getValue( ));
 }
}

The preceding piece of code illustrates how you can access an element's attributes once you have encountered its start tag.

18.5.3 Filtering a Stream

At this point, we know how to create an XML stream from a document, step over the stream of events, and provide custom handling depending on the event's type. The Streaming API also enables you to filter an XML stream so that only the events of interest are pulled from the stream. This means that when you do iterate over a filtered stream, you need to deal with only those events that have passed the filter. WebLogic's Streaming API allows you to register your interest in several ways. You can apply a filter based on an event's type, a subset of the elements, or the URI/type of a namespace. You even can apply a custom filter, whereby you decide which XML events will pass through the filter.

Earlier in this section, we saw an example of how you can use the newInputStream( ) methods on an XMLInputStreamFactory instance to create a stream from an XML document. The Streaming API supports an alternative two-argument version of the same methods. In this case, you use the second parameter to supply a filter. It could be a custom filter you have created or one of the default filters provided with WebLogic Server. The default filters are available in the weblogic.xml.stream.util package. Each of the TypeFilter, NameFilter, NameSpaceFilter, and NamespaceTypeFilter classes implement the ElementFilter interface.

The TypeFilter class takes a bit mask of all the event types that you want to let through. For instance, if you need to retrieve only the character and whitespace data in a document, you could apply the type filter as follows:

XMLInputStream stream = 
 factory.newInputStream(someSource, 
 new TypeFilter(XMLEvent.CHARACTER_DATA | XMLEvent.SPACE));

Now when you iterate over the XML stream, you'll encounter only events for whitespace and character data:

while (stream.hasNext( )) {
 XMLEvent e = stream.next ( );
 switch (e.getType( )) {
 case XMLEvent.SPACE: //Handle whitespace here and break
 case XMLEvent.CHARACTER_DATA: //Handle character data here and break
 default: // You will never reach here
 }
}

The NameFilter class filters the stream based on the name of the element. For example, if you need to deal with only inOne elements in the XML data, you would apply a name filter as follows:

XMLInputStream stream = 
 factory.newInputStream(someSource, new NameFilter("inOne"));

The NameSpaceFilter class lets you filter a stream based on the URI of a namespace, while the NamepaceTypeFilter class allows you to filter on both the namespace and type of an element. For example, if you want to retrieve only the XSLT start elements of an XSLT document, you would create a NamepaceTypeFilter as follows:

XMLInputStream stream = factory.newInputStream(someSource, 
 new NamespaceTypeFilter ("http://www.w3.org/1999/XSL/Transform",
 XMLEvent.START_ELEMENT));

18.5.4 Custom Filters

Custom filters can be applied to an XML stream in the same way as built-in filters. In order to create a custom filter, you need to register an instance of a class that implements the ElementFilter interface with the XML stream:

package weblogic.xml.stream;
public interface ElementFilter {
 public boolean accept(XMLElement event);
}

A custom filter needs to implement the accept( ) method, which determines whether an incoming XMLEvent can be let through.

Example 18-7 shows how to implement a custom filter that wraps multiple filters. The wrapping filter accepts an element if it is accepted by any one of its component filters.

Example 18-7. A custom filter

package com.oreilly.weblogic.xml.filters;

import weblogic.xml.stream.XMLName;
import weblogic.xml.stream.ElementFilter;
import weblogic.xml.stream.events.NullEvent;

public class OrFilter implements ElementFilter {
 protected ElementFilter [] filters;

 public OrFilter(ElementFilter [] filters) {
 this.filters = filters;
 }
 public setFilters(ElementFilter [] filters) {
 this.filters = filters;
 }
 
 // Only permit elements that pass any of the filters
 public boolean accept(XMLEvent e) {
 for (int i=0; i 
 if (filters[i].accept(e)) 
 return true;
 return false;
 }
}

We then can apply an OrFilter to an XML stream so that only elements with the name a or b are let through:

ElementFilter myFilter = new OrFilter(
 new ElementFilter[] {new NameFilter("a"), new NameFilter("b")});
XMLInputStream stream = factory.newInputStream(someSource, myFilter);

18.5.5 Positioning the Stream

The ability to skip ahead while iterating over a stream of parse events is a powerful feature of the Streaming API. You can skip ahead by n events or until a particular element has been encountered. Later in this chapter, we will examine how you can use this feature in conjunction with a buffered XML stream, whereby you can mark a position within the stream and later reset the stream to an earlier mark.

WebLogic provides three mechanisms for skipping within a stream. All of these methods are invoked on an XMLInputStream instance:

skip( ) and skip(int)

These methods allow you to skip ahead by one or a specified number of events. It does not matter what type of events are present in the stream. This method will just skip over as many events as you specify.

skip(XMLName) and skip(XMLName, int)

These methods allow you to skip ahead to an event with the specified name, or an event with the specified name and type. For instance, you could skip to the next end tag for element b as follows:

skip(ElementFactory.createXMLName("b"),XMLEvent.END_ELEMENT)

skipElement( )

This method simply skips over the start/end tag pair for the next element, avoiding all its subelements. If the current XML element contains no subelements, the method skips ahead past its end tag. For instance, if you create an XML stream using the following XML fragment " foo bar ", you can expect the following behavior:

  • If you invoke the skipElement( ) method just after processing the StartElement event for a, the stream will skip past the space and element b, and the next event will be the CharacterData event marking bar.
  • If you invoke the skipElement( ) method just after processing the StartElement event for c, the stream will skip past the character data, and the next event will be the StartElement event marking the start tag for a.

An XML stream supports an additional peek( ) method, which allows you to look ahead at the next event. Because this method returns the next XMLEvent, you then can make a decision based on the event's type or any information that you can extract from it.

18.5.6 Substreams

At any point while you are stepping over an existing XML stream, you can invoke the getSubStream( ) method on the XMLInputStream instance to return a copy of the next element and all of its subelements. The new XML substream will generate all XML events between (and including) the start/end tag pair for the next element. The parent stream remains unaltered and you can continue to iterate over the existing stream as before. If you want to step over the element that generated the substream, you can invoke the skipElement( ) method. The getSubStream( ) method returns a new XMLInputStream instance, which now can be used as the basis for parsing the next element and all its subelements. This substream extends over all XML events and stops only after it encounters an EndElement event that matches the initial StartElement event for the substream.

18.5.7 Buffered Streams

A buffered input stream can be created by wrapping an XMLInputStream instance by a BufferedXMLInputStream instance. When using a buffered XML stream, you can mark a particular position in the stream and later reset the stream back to the marked spot. Effectively, this feature allows you to reparse the stream this is very useful in situations in which you need to process the same XML fragment more than once.

The following code sample shows how you can mark a position within a stream, process that stream, and later reset it back to the earlier position:

XMLInputStreamFactory factory = XMLInputStreamFactory.newInstance( );
BufferedXMLInputStream bstream =
 factory.newBufferedInputStream(factory.newInputStream(someSource));
skip(4); // go somewhere
// mark the current position
bstream.mark( );
// perform some work on the stream
workOne(bstream);
// go back to our mark
bstream.reset( );
// perform some more work on the stream
workTwo(bstream);

18.5.8 Creating Output Streams

WebLogic's Streaming API also enables you to generate XML documents on the fly. Here you need to create an XML output stream and send various XML events to this stream. The Streaming API has an ElementFactory class, which provides factory methods for the various elements of an XML document: character data, comments, attributes, start and end tags, etc. Because XML data is structured hierarchically, you will be sending a linear stream of events based on a flattened representation of the XML. This means that for each element you need to construct a start tag, add any attributes, then build its body (which may include other elements) and finally its end tag.

The following code shows how to create an XML output stream, write XML data to the stream, and finally flush the contents of the stream:

XMLOutputStreamFactory factory = XMLOutputStreamFactory.newInstance( );
XMLOutputStream output = 
 factory.newOutputStream(new PrintWriter(System.out,true));
// ...
output.add(ElementFactory.createCharacterData("avi"));
// ...
output.flush( );
output.close( );

You can construct an output stream from a number of different sinks:

java.io.OutputStream and java.io.Writer

The XML data is written to the binary stream or character writer, as you would expect. In the earlier example, the XML output stream wraps the writer System.out, so the XML data is written to the console screen.

org.xml.sax.ContentHandler

The output is written to a SAX content handler, which eventually generates a stream of SAX events.

org.w3c.dom.Document

The XML output is written to a DOM document. The following code snippet shows how to create an XML output stream that wraps a DOM tree:

XMLOutputStreamFactory factory = XMLOutputStreamFactory.newInstance( );
Document doc = 
 DocumentBuilderFactory.newInstance( ).newDocumentBuilder( ).newDocument( );
XMLOutputStream output = factory.newOutputStream(doc);

Do not forget to use the flush( ) method on the output stream. Only then will the current contents of the XML output stream be written to the actual sink. For instance, if you are writing to a DOM document, you must flush the output stream before you start manipulating the DOM tree. If you don't flush the XML stream, you may have to suffer the consequences of a partially constructed DOM!

18.5.9 Writing to the Stream

The XML output stream provides various add( ) methods that enable you to generate XML data and write various elements to the stream. All elements are created via factory methods provided by the ElementFactory class:

output.add(ElementFactory.createStartElement("myelement"));
output.add(ElementFactory.createAttribute("a","1"));
output.add(ElementFactory.createCharacterData("Hello World"));
output.add(ElementFactory.createEndElement("myelement"));

The preceding code sample generates the following XML data:

Hello World

The output stream is nonvalidating, so you need to guarantee that the XML document is well-formed. For instance, you need to ensure that all elements are properly nested and that each start tag for an element has a matching end tag.

In fact, the add( ) method supports adding four types of objects: plain markup, elements, attributes, and an XMLInputStream. You can supply an XMLInputStream to the add( ) method this allows you to easily insert XML data from another source. Recall an XMLInputStream instance can wrap several different sources: a file, character reader, or DOM tree. The next example shows how you can insert XML data that has been parsed from a file:

output.add(ElementFactory.createStartElement("example"));
XMLInputStreamFactory factory = XMLInputStreamFactory.newInstance( );
XMLInputStream stream = factory.newInputStream(new FileInputStream(somefile));
output.add(stream);
output.add(ElementFactory.createEndElement("example"));

As you can see, writing to the XML output stream is quite straightforward. The ElementFactory class can manufacture all of the obvious elements in an XML document: start and end tags, attributes, character data, processing instructions, etc. It is quite cumbersome having to use the XMLOutputStream to write XML data. You need to generate the hierarchical XML data in a serial fashion, much like how the XML input stream delivers its XML events during a parse. This is quite unlike the more natural approach of constructing a DOM tree.

18 6 WebLogic s XPath API

Introduction

Web Applications

Managing the Web Server

Using JNDI and RMI

JDBC

Transactions

J2EE Connectors

JMS

JavaMail

Using EJBs

Using CMP and EJB QL

Packaging and Deployment

Managing Domains

Clustering

Performance, Monitoring, and Tuning

SSL

Security

XML

Web Services

JMX

Logging and Internationalization

SNMP



WebLogic. The Definitive Guide
WebLogic: The Definitive Guide
ISBN: 059600432X
EAN: 2147483647
Year: 2003
Pages: 187

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net