Java XML View Tutorial (Version 0.4)

Overview

Welcome to the JXV tutorial. This tutorial introduces the main features of JXV in a step-by-step format walking through a particular JXV application, the PhoneBook application. Before we go into the details of this particular example, this introduction section will explain what exactly is JXV, where it stands in the XML-binders arena, and where it should be used. The topics in the tutorial were ordered so that they can be read by a new user in the order they appear without having to jump forward. However, if you already know JXV you can use it as a reference as well.

Getting started

As a first step, you should make sure that the JXV distribution is working properly on your system. If you do not have the JXV distribution yet, you can download the latest release from http://jxv.sourceforge.net. JXV requires JDK 1.3 or higher. Once you get the distribution, run the order sample:

  1. Go into the "samples" folder in the JXV distribution.

  2. Run the JXVTester as follows (replace the "c:\temp" or "/temp" with some folder where you want the XML to be written).

    (the command-line arguments are explained in the JXVTester section).

  3. You should see something like: 
    Iteration 1 started...
    write operation: 2200 millis
    read operation: 281 millis
    rewrite operation: 47 millis
    written and re-written files are identical
    Original and read object matched

  4. The folder you specified in the command line should now contain two files, "OrderBatchFactory1.xml" and "OrderBatchFactory1-rewrite.xml". Open one of them and take a look at the generated XML.

Once you had gone through these steps and everything worked, you are ready to start using JXV.

What Is JXV?

JXV is a framework that translates Java objects into XML format. It supports the the two major XML APIs (SAX and DOM) and also supports reading XML back into objects. XML namespaces are fully supported and integrated into the configuration mechanism. JXV is highly configurable, and almost none of it's behavior is hard-coded in the source. An XML config file is used to load the factories that create views, and configure how those factories create the views. You can extend JXV by writing new view factories, although JXV's existing factories are flexible and are sufficient for most purposes.

Where JXV stands in the XML-binders arena

JXV is a type of XML Data Binder. There are two major types of data binders:

  1. Object-to-XML mappers. These binders take a given set of Objects and produces XML to represent these objects. The center of the development process in this case are the Objects.
  2. XML-to-Class mappers (e.g. schema compilers). These binders take a given XML format (for instance described in an XML schema) and produce classes to represent this format. The classes can read/write the XML format they represent. The center of the development process in this case is the XML format.

It is usually clear which type of binder you need. If you already have objects and you want to treat them as XML for some reason (several possible reasons will be given below) you need a mapper of the first kind. If you have an XML format, and you want to represent it using classes so you can process it in Java, you need a mapper of the second kind.

JXV fits into the first category. As it's name implies, it gives Java objects "XML views". In this category there are also two sub-categories. Some mappers generate a "normal" XML format to represent your objects. For instance, a PhoneBook object with a collection of contacts might look something like this:

<phonebook>
    <contact>
        ...
    </contact>
    ...
</phonebook>

Other mappers generate some specific object-markup format of their own. The same example as above might look something like:

<object type="mypackage.PhoneBook>
    <field name="contacts" type="java.util.Collection">
        <object type="java.util.LinkedList">
            ...
        </object>
    </field>
</object>

This second type is not really a mapper, but rather a type of a serialization framework that happens to use XML as it's serialization format. JXV again fits into the first category. The second category is useful mainly when you want to read/write your objects and you prefer a text-based format for some reason. It is very hard to process the mapper-specific format in languages other than Java, or to apply XML applications such as XSLT and XPath on it. Which brings us to...

When To Use JXV

JXV can be used anywhere where you can benefit from an XML representation of Java objects. In fact, JXV was designed with this goal as a key focus (in contrast to just XML read/write), and it supports some unique features that promote this goal and are not available in most XML mappers. The reflective DOM tree is an example of this. It is described the following section. So, the question is, when can you benefit from having an XML representation of your objects? Here is a list of some things you might want to do where an XML representation of objects can be helpful:

I'm sure there are lots of other scenarios not listed here. XML is very popular and there is an ever growing number of standards and applications that facilitate the processing of XML documents. It is the goal of JXV to act as a bridge between the Java object world and the XML document world so that these standards and applications can also be applied to object graphs.

The Basic Concepts Of the Framework

In this section you will learn the basic concepts and terminology of JXV. Perhaps the most basic concept is the concept of a View Factory. View factories are factory objects that create, given a specific configuration context (see below), an XML view or a View Reader. There are two types of XML Views:

  1. DOM Views: a DOM view consists of a list of DOM nodes (e.g., elements, text) and a set of DOM attributes. This is an important point which sometimes contradicts the intuition of new users, who expect every object to be represented by exactly one node. The reason for this conflict in intuition is a basic mismatch that occurs when you translate objects into XML (see the note below). But first let's define the other type of XML view.

  2. SAX Views: a SAX view is the SAX-equivalent of a DOM view. It has methods to emit it's list of SAX events to a SAX event listener, and a method to return it's attribute set. SAX Views are usually more efficient if you just want to write the XML once.

View Readers are objects that take a stream of SAX events to treat as an XML view of an object, and read it back into an object. JXV's view readers generally implement a single-lookahead algorithm. This means that in contexts where the reader doesn't know which view will come next (i.e. it has multiple alternative views), it will look only one event ahead when deciding which view is in fact the correct one. An example may clarify this. Consider an Expression class and two subclasses, AndExpression and OrExpression. When the JXV reader tries to read an Expression, it would first try to read it as if it were an AndExpression. If the first event matches (for instance the first element in the AndExpression view is the same as the next element in the SAX stream), then the reader would now become sure that it is in fact reading an AndExpression. If the rest of the view isn't correctly structured, it would throw an exception. However, if the first element doesn't match the first element of the AndExpression view, then the reader would try to interpret the view as an OrExpression. Single-lookahead algorithms are generally more efficient than algorithms that remember chunks of the stream while reading it, because they don't waste any memory. At every point in the reading process, only one event has to be in the memory. The burden placed on you as a designer of XML views is not very serious in the XML case. All you have to do is make sure that if there are two or more alternatives for the type of view that will be read, the first element in each alternative is different.

A note about error messages from the readers:
When you get an error message from the reader saying that an element X was expected, keep in mind that when the reader framework has several options to proceed, it will try them all before giving an error. So, for instance, if you have an XML file that looks like this:
<phonebook>
    <contact>...</contact>
    <contact>...</contact>
    <kontact>...</kontact>
    <contact>...</contact>
</phonebook>
Then JXV would read the first two contacts, then upon seeing the <kontact> element it would decide that there are no more elements in the contacts collections. So it would look for what it expects to come after the contacts collection XML view, which is </phonebook>. So the error message would say "expected </phonebook>, got <kontact>". Try to remember this when you see JXV's error messages.

There is no need to support a DOM-equivalent of View Reader, because any DOM tree can easily be transformed into a series of SAX events. From this you may also deduce that the DOM Views are also redundant. But this is not the case. JXV's DOM views are not mere static DOM trees, they are reflective. This means that when the underlying object model changes, the DOM view changes with it. It also means that the information is not duplicated in the tree: the tree queries that object model for the actual data whenever it needs it. The tree is also dynamic and doesn't have to be completely loaded in memory. Only the parts that are needed are loaded on demand. To improve performance, parts of the tree that are loaded are weakly-referenced so that if the GC doesn't need them, they can be reused the next time they are needed. Another feature of the reflectiveness of the tree is that every node in the tree can always be traced back to the object to whose view it belongs. If you are running XPath or XQuery expressions on the tree, this is very handy.

A note about the mismatch between XML documents and objects grpahs:

As I mentioned above, there is a basic mismatch between XML documents and object graphs that causes some counter-intuitive results. For instance, consider a Person object. This object has a list of PhoneNumbers each giving a single phone number of this person. An XML file representing this model might look something like this:

<person>
    <phone-number>
        embed the view of the PhoneNumber object here
    </phone-number>
    ...
</person>

The <phone-number> element itself is not a part of the view of the PhoneNumber view, but rather a part of the view of the person. This is understandable. For instance if the Person also had a "myPhoneNumber" property we might want a <my-phone-number> elements in the person view to represent it. It doesn't make sense for this element to be a part of the PhoneNumber view, because it depends on the name of the property in which the PhoneNumber object appears, not the PhoneNumber object itself.
Even though it makes sense for the <phone-number> element to be a part of the Person view rather than the PhoneNumber view, it is counter-intuitive in some contexts. A particularly important example is the evaluation of XPath expressions. When you evaluate an expression like "//phone-number" you want to choose all the PhoneNumber objects, not all the Person objects. So a method needs to be devised to differentiate between the view that owns the node and the view that is contained in the node. JXV does this by providing two methods in the JXVNode interface: getOwnerView and getContainedView. Deciding what is the contained view is a heuristic process, because a node is not guaranteed to contain a view or to contain only one view. If the node does not contain exactly one view, an exception is thrown.

It is also important to realize that some times it does make sense for the "main element" to be part of the view of the contained object rather than the containing object. This is often the case when dealing with class hierarchies that use inheritance. For instance, suppose you have an Expression class and two subclasses, AndExpression and OrExpression. You want the subclasses to be represented by an <and> and <or> element, respectively. However, the class that contains an expression can't know what type of expression it holds (assuming the property type is the superclass, Expression), and therefore can't decide how to name the element. So in this case the main element really is the responsibility of the Expression itself, not the object that contains it. In these cases, you would need to use getOwnerView rather than getContainedView when you select an <and> or <or> element with an XPath expression.

JXV Configuration

The JXV configuration file has a pretty simple generic format, where each specific type of configuration uses it's own namespace to prevent collisions. This allows new View Factories and other components that extend JXV to pass on their configuration as a part of the configuration file. This is an important feature, because maintaining a different set of files of each component can quickly become a maintenance nightmare. It also eliminates the need of each view factory implementer to devise and implement his/her own configuration mechanism, making it easier to write JXV extensions.

The generic format is as follows: the root element has name <config:jxv-config> (the "config" prefix in this tutorial is assumed to be mapped to the JXV config URI, "http://jxv.sf.net/config"). This element can have a "config:priority" attribute specifying the default priority of config items in the file. If this attribute is missing, the value 50 is used by default. The element contents of the root element have a format called the "config context format". It has a separate name because it can also appear inside certain other elements. There are two types of children in this format:

  1. Include instructions: <config:include location="..." />. The location can be a location in the classpath of the ClassLoader that loaded the JXV libs, or a file name. The result of this instruction is the inclusion of all the config items (see below) in the specified config file to the config file where the include instruction appeared. The config items act as if they appeared where the include instruction appeared, accept for having the default priority defined in their own file rather than the default priority defined in the including file.
  2. Config Items. A config item can be any element with a URI different than that of the JXV config. Each component that is configured through the config file has it's own URI which it uses to find it's config items. Config Items have a certain order that determines which of them will be used when a component asks for it's config. Each Config Item has an associated priority number. The lower the number, the higher the priority. If two Config Items have the same priority, whichever comes first in document order is returned first. Priorities lower than zero should not be used by end-users. They are reserved for internal use by view factories. Also, it is not recommended to use priorities higher than 100, because the default configuration files of some view factories use priorities in that range, and setting a higher priority value will mean that the default configuration will take precedence over your configuration.
    The config item element can have two attributes that are interpreted by the JXV config system:
    1. config:category: this specifies the category of classes to which the configuration given in the config item applies. The expression language used to describe these class categories is explained in the Categories Appendix.
    2. config:priority: overrides the default priority of the config item.

The config context format can also appear as the content of certain types of elements, allowing you to "specialize" the main config and override some of it's definitions in certain contexts. When the Config Items appear as children of the config file's root element, they must have a config:category attribute. If they appear in an overriding context the attribute can be omitted, in which case the Config Item is assumed to be applicable to all classes. The config:priority attribute is always optional. If it does not appear, the file's default priority is used.

The last concept we will cover in this section is the concept of XML templates. Most of the major view factories (document, JavaBeans, array, Collection and Map views) use XML templates. As a JXV user, this means to you that all of these view factories are configured in a similar way. However, the concept of XML templates is far more important than just a configuration method. It is a simple and powerful abstraction for describing object-to-XML mappings. JXV has a package, org.jxv.template, for dealing with this abstraction. It allows users to read and write templates (in both SAX and DOM forms). In fact, this library is the only place in JXV where XML content other than Text nodes (SAX character events) is produced (for writing) or consumed (for reading). This means that new view factories can be developed almost without dealing directly with XML. If you ever write your own view factory, this is a big plus.

So, just what are XML templates? The basic idea is quite simple and consistent with the definition of JXV’s XML views. Here is an example of a template configuring the DOM view of a person:

<bean:root example-attr=”123”>
    <first-name>
        <bean:property name=”firstName” />
    </first-name>
    <last-name>
        <another-element>
            <bean:property name=”lastName />
        </another-element>
    </last-name>
</bean:root>

 The template can be instanciated to give an XML view (more accurately this is called an “XML fragment”, which is just like a view, only it doesn’t have to represent the view of one specific object; it is simply a set of attributes and a list of child nodes). The <bean:root> element itself is not a part of the instanciation. It is used merely as a convenient holder for attributes and child nodes. The attribute set of the instanciated template view will contain the “example-attr” attribute. The node list will contain exact copies of <bean:root>’s children, with one crucial difference: the “command nodes”, in this case <bean:property> are replaced with some other XML fragment. In this case, that XML fragment is the XML view of the value of the named property. The attribute set of this view is copied to the attributes of the parent (<first-name> or <another-element>), and the node list becomes a list of children in the parent.

 The general concept of XML templates is just this, only instead of <bean:property> you can have some other set of command nodes. If a command node appears as a direct child of the root node, then it's attribute set is added to the attribute set of the template instanciation and it's list of nodes is added to the template instanciation's list of nodes. Otherwise, it's attribute set is copied to it's parent elements, and it's list of child nodes is added the the parent's list of child nodes.

There are two standard command nodes that are supported by all templates. These are used to create XML constructs that are otherwise impossible to create, because of the structure of XML. The first, <template:attr> is used to create an attribute whose content is specified by a command node. This is impossible without <template:attr> because attributes in XML only contain text, so they can't contain a command node. Here is an example of the use of the <template:attr> command node:

<bean:root>
    <template:attr name="size">
        <bean:property name="size"/>
    </template:attr>
</bean:root>

The XML view of the bean (i.e. the template instanciation) will contain an attribute called "size" whose value is the XML view of the "size" property. You should make sure that the DOM view of the property only contains text, because that's the only thing allowed inside an attribute value.

The second standard command node is <template:element>. This node can be used when you want the element name itself to be specified by a command node. If the name is static, don't use this element. Simply create an element with the desired name and place the command node inside it. Here is an example of using <template:element>:

<bean:root>
    <template:element>
        <name>
            <bean:property name="prop1"/>
        </name>
        <content>
            <bean:property name="prop2"/>
        </content>
    </template:element>
</bean:root>

The <name> and <content> elements are not themselves part of the template instanciation. The content of the <name> element (command nodes being replaced with their XML fragment) is used for the element's name. The content of the <content> element is used of the element's content. Attributes appearing in the <content> element become attributes of the created element. Again, you should make sure that the XML view of the property used for the name only contains text.

Note: the dynamically generated element name cannot contain a namespace prefix. If you want the element to have a namespace prefix, you can declare it in a "prefix" attribute inside the name element. The value of the "prefix" attribute must be a prefix with some previously declared namespace binding.

The PhoneBook Application

The PhoneBook object model is located in the test.phonebook package under the samples folder. Take a moment to look at the model. It is a very simple JavaBeans model with the following structure: PhoneBook is the main class. It has a title property and a contacts property which is a collection of Person objects. Each Person has some personal info (name, etc), home and work Address properties, a collection of emails (Strings) and a map of PhoneNumbers. The keys of the map are the type of number, e.g. work, home, mobile, etc. PhoneBookFactory is a simple factory class that creates random PhoneBook instances given three parameters: number of people in the book, max number of emails per person and max number of PhoneNumbers per person.

First let's run JXV on this object model without any configuration and see what happens:

java -classpath .;../lib/jxv.jar org.jxv.test.JXVTester -rw c:\temp test.phonebook.PhoneBookFactory 1 5 3 3

Iteration 1 started...
write operation: 2000 millis
JXV exception occured during JXV parsing process on line 2, column -1: Instanciation of class test.phonebook.Country failed
    at org.jxv.reader.JXVSAXReader.startElement(JXVSAXReader.java:189)
    at org.apache.crimson.parser.Parser2.maybeElement(Parser2.java:1488)
    at org.apache.crimson.parser.Parser2.content(Parser2.java:1779)
    at org.apache.crimson.parser.Parser2.maybeElement(Parser2.java:1507)
    at org.apache.crimson.parser.Parser2.content(Parser2.java:1779)
    at org.apache.crimson.parser.Parser2.maybeElement(Parser2.java:1507)
    at org.apache.crimson.parser.Parser2.content(Parser2.java:1779)
    at org.apache.crimson.parser.Parser2.maybeElement(Parser2.java:1507)
    at org.apache.crimson.parser.Parser2.content(Parser2.java:1779)
    at org.apache.crimson.parser.Parser2.maybeElement(Parser2.java:1507)
    at org.apache.crimson.parser.Parser2.parseInternal(Parser2.java:500)
    at org.apache.crimson.parser.Parser2.parse(Parser2.java:305)
    at org.apache.crimson.parser.XMLReaderImpl.parse(XMLReaderImpl.java:442)
    at org.jxv.reader.JXVReaderHelper.read(JXVReaderHelper.java:66)
    at org.jxv.reader.JXVReaderHelper.read(JXVReaderHelper.java:72)
    at org.jxv.test.JXVTester.inputXML(JXVTester.java:111)
    at org.jxv.test.JXVTester.main(JXVTester.java:204)
Caused by: org.jxv.JXVException: Instanciation of class test.phonebook.Country failed
    <more stack traces...>

Don't worry, you are supposed to get this error. Go to "c:\temp" and check out the file that JXV created. It seems alright, but there are some things that could be better. In the following sections you will see many configuration options that tweak the way the view will look. Which is best for you depends on your own personal preferences and the requirements of your project.
What we actually ran in this example is JXVTester, a command-line utility that comes with the JXV distribution. Following is a description of this tool and another similar tool. We will be using JXVTester throughout this tutorial, so make sure you understand it's basic operation before proceeding.

JXVTester

The JXV package comes with a simple command-line tool, org.jxv.test.JXVTester, designed to help users try out JXV on their own object models without having to write JXV-related code. By implementing a simple factory interface, you can generate XML-views for your objects without studying the JXV API. Once you feel comfortable with JXV's features and configuration mechanisms, you should be able to start using JXV in your own code in a matter of minutes.

The basic functionality of JXVTester is:

Repeat for a specified number of iterations:

  1. Construct an object using a factory.

  2. Write it to the output folder.

  3. Apply some XSLT transform to it and write the result to the output folder (optional).

  4. Read the object back and compare it to the original (optional).

  5. Write the object that was read to the output folder again, and compare the two files you wrote to make sure they are identical (optional).

As you can see, other than simply applying JXV to an object model and writing out an XML representation, JXVTester can also perform some level of integrity tests, such as making sure that the XML can be read back into an object. It also gives you some performance and timing diagnostics. When looking at those timing figures, keep in mind that the first iteration involves a lot of configuration-loading, cache-filling, etc., so it is usually a couple of seconds longer than the following iterations (even if the following iterations only take a couple of milliseconds). Therefore, in order to get reliable results about the timing you should use at least two iterations. It's generally a good idea to use even more, in order to see how memory usage and other factors affect the performance as the application progresses.

JXVTester's syntax is:

java org.jxv.test.JXVTester [-DOM] [-r|-rw] [-xslt file-name] [-configname config-name] output-dir factory-class iterations [factory args]

If you're planning to use JXV for reading XML as well as writing, you might also be interested in testing how the JXV readers read files that you generate yourself, rather than just read files that JXV generated. In order to do that, a separate utility is provided:

JXVConsoleReader

The JXVConsoleReader is a console application which reads XML views of objects from XML files. It is capable of printing the XML view of the resulting object graph to a specified file or stdout (so that you can examine the result), and compare the resulting XML view with the original file. However, note that the comparison performed is byte-to-byte, so it's possible that equivalent XML documents won't match because of ignorable whitespaces and other physical differences. For more complex, "semantic" comparisons you should output the file and use an external diff util.

JXVConsoleReader's syntax is:

java org.jxv.test.JXVConsoleReader [-print output-file|"stdout"] [-compare] [-configname config-name] file class

Now that we've seen the basic utilities we will use, we can start building a configuration file for our PhoneBook example.

Factory Config and Flat Config

The first thing we ought to do is fix that exception that is stopping us from reading the file. The problem, as indicated in the error message, is in test.phonebook.Country. This class is an implementation of a type-safe enumeration. It's not a JavaBean. Specifically, it doesn't have a public no-arguments constructor. As a result, when JXV tries to read it using the default factory, the JavaBeans view factory, it gets an exception when it tries to instanciate the class. What we will do is configure JXV to use the flat factory instead, which is used to create "flat" text views for objects.
The default flat factory behavior is to use the toString() representation as the text of the view, and to use a constructor with a single String argument to read the view. Since our Country object doesn't have such a constructor, we will need to define a marshaller that will do the reading/writing of Country objects. Marshaller classes must extends the abstract org.jxv.flat.Marshaller class. The Country class already has a static inner class called Marshaller that marshals it. The marshaller class doesn't have to be an inner class, it is just a convenient naming scheme. If you don't want to put JXV-related code in your business objects you can put the marshaller in a separate class or even a separate package.

Here is the new config file:

<?xml version="1.0" encoding="UTF-8"?>

<config:jxv-config xmlns:config="http://jxv.sf.net/config"
                            xmlns:flat="http://jxv.sf.net/flat"
                            xmlns:factory="http://jxv.sf.net/config/factory">

    <factory:root config:category="test.phonebook.Country" factory-class="org.jxv.flat.JXVFlatFactory" />

    <flat:root config:category="test.phonebook.Country" marshaller="test.phonebook.Country$Marshaller" />

</config:jxv-config>

The first line, sais: "create a config item for the factory URI that would match the test.phonebook.Country class and configure it to use the org.jxv.JXVFlatFactory factory". The general format for factory config items is no more complicated than this. The only attribute it defines is "factory-class", which should be a name of a view factory class.
The second line sais "create a config item for the flat URI that would match the test.phonebook.Country class and configure it to use the test.phonebook.Country$Marshaller marshaller". Again the general format for the flat config is no more complicated than the single required attribute "marshaller" that should contain the name of the marshaller class.

In order to be able to use the factory config item, you need to know what XML view factories there are. The following table summarizes information about the view factories available in the JXV distribution. More details are given in the sections that follow.

Factory class Description
org.jxv.bean.JXVBeanFactory A factory for generating XML views of JavaBeans. This is the default factory for most classes.
org.jxv.container.JXVCollectionFactory A factory for generating XML views of Collections.
org.jxv.container.JXVArrayFactory A factory for generating XML views of arrays.
org.jxv.container.JXVMapFactory A factory for generating XML views of Maps.
org.jxv.flat.JXVFlatFactory A factory for generating "flat" text views.
org.jxv.TypeDecoratorFactory A factory that decorates the view of the next configured factory with a "class" attribute. You only need to use this factory directly in extremely rare circumstances, as JXV usually configures it automatically where needed.

A note about primitive types:
JXV is implemented using reflection. This means that primitive types are not accessed directly: the reflection classes wrap and unwrap them into primitive wrapper classes whenever required. Consequently, XML views are always created for Objects, never for actual primitives. So when you are creating a config item for the factory namespace or a config item for a view factory, and you want it to apply to some primitive type, you need to put the wrapper type in the config item's category, not the primitive type itself.

What we have done in this configuration file is made the flat factory be the default factory for generating views of our Country class, and configured the flat factory to use our marshaller to read/write the text representation of the country. These two tasks make most of the work you do in configuring JXV: decide which factory will be used to create a view for an object, and separately configure that factory with it's own specific options.

Lets run JXVTester like we did last time, but this time tell it to use our new configuration file:

java -Dorg.jxv.config.config-files=.\test\phonebook\config1.xml -classpath .;../lib/jxv.jar org.jxv.test.JXVTester -rw c:\temp test.phonebook.PhoneBookFactory 1 5 3 3

Iteration 1 started...
write operation: 1484 millis
read operation: 169 millis
rewrite operation: 15 millis
written and re-written files are identical
Original and read object matched

Notice the "-Dorg.jxv.config.config-files=..." in the command line. This is the part that passes the configuration file to JXV. We see that this time the tester did finish running without exception. As we've seen in the JXVTester section, what the tester does when given the "-rw" option is this: it writes the file, then reads it, then writes the object it read again. This way you can make sure that the read/write process preserves the structure of your XML. The tester also compares the object it read to the original version of your object.

In this case we see that the XML version of your original object was the same as the XML version of the object that was read from the file, and the objects  matched (i.e. the equals() method return true). However, the objects are not exactly identical. The type of the collection in the original object were ArrayList, while the types in the read object are LinkedList. When JXV doesn't know the type of a Collection, it guesses (by default) that it is a LinkedList. Usually if JXV writes an object and sees that it's default guess (when reading) would be wrong, it writes a "class" attribute with the actual class of the object. However, this can only be done when the view of the object has "it's own" parent element, i.e. an element that contains no other views. If the view doesn't have it's own parent element, it can't write the class attribute because it would get mixed up with the class attributes of the other views in the same parent. In this example, the parent element of the contacts property is the "phonebook" element, which is also the parent of the "phonebook" view. This is because by default JXV inlines the elements of a collection directly in it's parent view rather than create a parent element that would contains all the elements. There are two ways to fix the problem. One is to simply give the collection it's own parent element. For instance we could have a "contacts" element that would contain the view of the collection. That way JXV could safely write the class attribute. The other option is to tell JXV that when it reads the contacts property, it should "guess" it has the type ArrayList rather than LinkedList. We will see how to do both these things in a later section, but first we need to go over the basics of bean configuration.

Bean Factory

Here is a part of the XML generated by JXVTester with our new config file:

<?xml version="1.0" encoding="UTF-8"?>

<phone-book>
    <contact class="test.phonebook.Person">
        <email class="java.lang.String">Random email #267</email>
        <first-name>Person #0</first-name>
        <home-address>
            <address>Random street #681</address>
            <city>Random city #535</city>
            <country>Israel</country>
            <zip-code>Random zipcode #551</zip-code>
        </home-address>
        <last-name>Random last name #256</last-name>
        <mid-name>Random mid name #193</mid-name>
        <phone-number key="Location0" class="test.phonebook.PhoneNumber">
            <area-code>Random area code #985</area-code>
            <number>Random number #467</number>
        </phone-number>
        <work-address>
            <address>Random street #500</address>
            <city>Random city #683</city>
            <country>United-States</country>
            <zip-code>Random zipcode #236</zip-code>
        </work-address>
    </contact>
    more <contact> tags...
    <title>Random title #991</title>
</phone-book>

The first thing we will change is move the title to an attribute in the "phone-book" element. We've already seen something similar in the templates section, but there is one more factor at play here. We want to override the default configuration for the PhoneBook object, but we don't want to be forced to write all of it. Particularly, we don't want to touch the "contacts" property. The bean configuration has an element called "apply-defaults" that works like a macro, bringing in the default configuration for properties of a bean. If you write <apply-defaults apply-to="property"/> then the default configuration for "property" will be inserted. If you don't specify an "apply-to" attribute, then default configurations will be generated for all the properties that have not appeared in the configuration prior to the apply-defaults element. So, here is what we add to our configuration file:

<bean:root config:category="test.phonebook.PhoneBook">
    <template:attr name="title">
        <bean:property name="title"/>
    </template:attr>
    <bean:apply-defaults/>
</bean:root>

First we create an attribute named "title" and put the view of the title property in it. The command node <bean:property> represents the value of a property of the bean. You can specify a "name" attribute with the name of the property, or "getter" and "setter" attributes with the name of the getter method and setter method of the propety. This allows you to read/write properties whose names do not follow the standard JavaBeans naming conventions.
After creating the attribute with the view of the "title" property, <bean:apply-defaults> is used to include the configurations for all the other properties. The title configuration won't be generated again, because the title property has already appeared in the configuration. <bean:apply-defaults> is not a command node. It doesn't have any implications at runtime. It's merely a macro for generating configuration, and it is expanded when the configuration is loaded. You can use it without fear of performance issues. You can use XSLT templates to override the way apply-defaults generates default configurations for properties, but since this is quite advanced and not commonly needed we'll come back to it later.
If you want to skip a certain property, i.e. have apply-defaults ignore it, without including it yourself in the config file, you can use <bean:skip property="..."/>. The named property will be ignored by apply-defaults.

A note about apply-defaults and namespaces:
The elements generated by apply-defaults has the same namespace prefix and URI as their parent element. If you want apply-defaults to generate elements with a different prefix, you can specify it in a "prefix" attribute in the <apply-defaults> element. Attributes don't use namespace prefixes by default, but you can override this behavior by specifying your own XSLT template for generating property configurations as noted above.

Run JXVTester in the usual way, this time using config2.xml. Exam the resulting XML file. You should now see a "title" attribute instead of the title element we had before. Now we'll try something a little more complicated involving both bean configuration and container configuration. "container" is a configuration namespace that we use to configure the views of both Collections and arrays.
Instead of having "email" elements right in the "contact" element, we will place them in a separate "emails" element. We'll start by just putting the emails property in it's own element like we did with the title element. Here is the relevant config:

<bean:root config:category="test.phonebook.Person">
    <emails>
        <bean:property name="emails"/>
    </emails>
    <bean:apply-defaults/>
</bean:root>

Run JXVTester with config3.xml and examine the XML it outputs. There are a couple of things to notice. First, we're suddenly getting an "item" element for each email instead of an "email" element. Second, now that the emails collection has it's own element, JXV can write it's runtime type in a class attribute so it can correctly read it. The objects still don't exactly match (although the equals() method does return true and so JXVTester prints they are identical), because the contacts property is still being read as a LinkedList instead of an array list. We'll fix that using a different technique later.

Container Factory

The reason we are getting that "item" element is that it is the default view for collections. The container itself doesn't know that it is the value of a property called "emails", and therefore it can't know what element name to use. Usually the default bean configuration passes this information to the container. I should point out at this point that if we replaced <bean:property name="emails"/> with <bean:apply-defaults apply-to="emails/> the problem would be solved because the apply-default mechanism would generate the correct configuration. But in order to learn how to configure collections and pass configuration data from one config item to another, we'll do it ourselves. Here is the correct configuration:

<bean:root config:category="test.phonebook.Person">
    <emails>
        <bean:property name="emails">
            <container:root>
                <email>
                    <container:item/>
                </email>
            </container:root>
        </bean:property>
    </emails>
    <bean:apply-defaults/>
</bean:root>

Run JXVTester with config4.xml and see how the "item" elements have now been replaced by "email" elements.

There are a couple of new things going on here. First of all, we see that the <bean:property> element now has children. This was briefly described in the introduction section as a way of "specializing" the main JXV configuration. Basically, <bean:property> or any other command node that represents the XML view of some object, can have children with the same format as the main JXV config (apart from the fact that the config:category attribute is optional, and if omitted the config item applies to all classes). When XML views are created for the objects represented by these command nodes, the config items that appear as children of the command node are consulted before the main configuration (unless the items in the main configuration has a higher priority value). So you can, in effect, override some of the configurations you have in your main config with special versions that will be used when evaluating a particular property.

Note: command nodes that represent the XML views of other objects are called "proxy command nodes" because they serve as proxies for other XML views. All proxy command nodes support the format described above. There is also a mechanism which generates default configurations for proxy nodes which is related to JXV's "guesses" of what types to read, and decisions of when to write "class" attributes. You can override this mechanism, like the mechanism for generating default property configuration in beans. Since this is advanced and not commonly needed, we'll touch that later.

The second new thing in this example is the container configuration. We create a config item for containers which will determine how containers format their views when generating views for the emails property. A container configuration is just like a bean configuration, only:

The explanation above should clarify what is going on. We configure the emails collection so that each item in it (i.e., each email) would instanciate the template, creating an "email" element containing the XML view of the email.

Proxy Reader Config

There is one more thing to change in the configuration of the emails property. We will get rid of that "class" attribute that gets added to each item in the collection. JXV has a pretty simple mechanism for deciding when to write a class attribute. The config context that you have in every proxy command node has a list of "substitute-reads", which determine which types of objects JXV will try to read when reading the view of that command node. By default this list is generated to contain a single "reasonable" value. For instance, in the config context of a <bean:property> command node you would have a substitute-read with the type of the property. In a <container:item> of an array you would have the type of the array's component. When there is no reasonable guess that JXV can make on it's own, for instance in a <container:item> of a Collection, the substitute-read type is Object, but that is a mere formality because the reader ignores substitute read types that are abstract classes, interfaces, or java.lang.Object.
You can add your own list of substitute reads to a config context using the <reader:root> element as shown in the following example. Your list is then added to the default list: you don't have to repeat the default values.
When JXV writes an XML view as the value of a proxy command nodes, it checks if the runtime type of the object is in the substitute-reads list for that node. If it is, then the type would be tried by JXV anyway, so there's no reason to write a "class" attribute. If it isn't, then a "class" attribute must be written, otherwise JXV won't be able to decide which type to read.

A note about recursive expansion of substitute reads:
Substitute read lists are expanded recursively in the following way: for each class in the substitute reads list, JXV looks up all the <reader:root> config items whose category matches the class. The substitute-read lists from all these items is added to the list of substitute-reads, and processed recursively. JXV correctly resolves cycles involved in this recursive process.
You can use the recursive expansion to define global substitute reads for some types. For instance, if you have an interface that you use in your project and you have three implementation classes, you can define these classes as substitute reads for the interface in the main configuration context. Then you wouldn't have to define them separately each time you create a proxy command whose base type is the interface. The base type would be included in the substitute-reads list by default, and then expanded to include your three implementation classes.

A note about specifying substitute reads with flat views:
The JXV reader architecture has no way of knowing that a certain XML is not a flat view, because even if there is no text, it could be a flat view with an empty String. So, when you define a substitute-read of a text view, be sure that it the actual runtime type of the object. Otherwise JXV would read an incorrect type. If you can't know the actual runtime type of the object, let JXV write a "class" attribute.

Now we will configure the <container:item> command node with the substitute-read "java.lang.String":

<bean:root config:category="test.phonebook.Person">
    <emails>
        <bean:property name="emails">
            <container:root>
                <email>
                    <container:item>
                        <reader:root substitute-reads="java.lang.String"/>
                    </container:item>
                </email>
            </container:root>
        </bean:property>
    </emails>
    <bean:apply-defaults/>
</bean:root>

Run JXVTester with config5.xml. In the XML output you should see that the <email> element now has no "class" attribute. JXV knows the type from the substitute read we have defined, so it doesn't have to write it as an attribute.

We will now use substitute-reads again, this time to preserve the runtime type of the contacts property. You may remember the "class" attribute doesn't get written because the contacts XML view doesn't have "it's own" parent, and it's class attribute could be confused with the class attribute of the phonebook itself. We have seen one solution to this type of problem already: putting the collection in it's own parent element. The other solution, the one we will use now, is to specify with substitute-reads which type of collection JXV should read. Here is the new configuration for the PhoneBook bean:

<bean:root config:category="test.phonebook.PhoneBook">
    <template:attr name="title">
        <bean:property name="title"/>
    </template:attr>

    <bean:property name="contacts">
        <reader:root substitute-reads="java.util.ArrayList"/>
        <container:root>
            <contact>
                <container:item>
                    <reader:root substitute-reads="test.phonebook.Person"/>
                </container:item>
            </contact>
        </container:root>
    </bean:property>

    <bean:apply-defaults/>
</bean:root>

Run JXVTester with config6.xml. You can see the <contact> elements now have no class attribute, because we have specified their class in the substitute-reads. Also, the objects read by the JXVTester are now truly identical to the originals, now that we've taken care of both "problematic" collection properties. There's not much new in this example. Notice how we pass more than one config item in the <bean:property> element. We pass one config item for specifying the class that should be used when reading the property, and another config item for configuring the view of the container.

Map Factory

We will now take a look at the map config item. It is used to configure XML views for Maps. The general format is very similar to that of container configurations, the only difference being that the <container:item> command node is replaced by <map:key> and <map:value>, representing the XML views of the key and value of a Map entry, respectively. These command nodes are proxy command nodes as well.

As you can see in JXVTester's XML output, the default format for a map includes a "key" element containing the key, followed by the XML view of the value. We will override this default and configure the phoneNumbers attribute of the Person bean to generate for each entry a <phone-number> element with a "location" attribute containing the view of the key, and children containing the view of the PhoneNumber object. There is nothing particularly new or complicated about this config. It's just a combination of some of the stuff we've seen. Here is the new config item for person (the emails property config is unchanged):

<bean:root config:category="test.phonebook.Person">
    <emails>
        <bean:property name="emails">
            <container:root>
                <email>
                    <container:item>
                        <reader:root substitute-reads="java.lang.String"/>
                    </container:item>
                </email>
            </container:root>
        </bean:property>
    </emails>

    <bean:property name="phoneNumbers">
        <map:root>
            <phone-number>
                <template:attr name="location">
                    <map:key>
                        <reader:root substitute-reads="java.lang.String"/>
                    </map:key>
                </template:attr>
                <map:value>
                    <reader:root substitute-reads="test.phonebook.PhoneNumber"/>
                </map:value>
            </phone-number>
        </map:root>
    </bean:property>

    <bean:apply-defaults/>
</bean:root>

Run JXVTester with config7.xml. The new part is the phoneNumbers property config. We configure the map view factory to create a <phone-number> element with the relevant information for each entry in the map. Also notice that we define substitute reads for both the value and the key so that the XML doesn't get "polluted" with "class" attributes. That's about as complicated as map configurations get.

Document Factory

If you've understood all the configurations demonstrated up to this point, you should know which factory generates each XML node (i.e. elements, attributes, and text) except for one node. The only thing we haven't gone over yet is who generates the root element, the "phonebook" element. This is not a part of the XML view of the phone book itself. The factory that generates this element is the document factory. This is the last major view factory you need to know of. It has a fairly simple configuration. All it does is act as a wrapper around a normal XML view, wrapping it in an XML document.
Like the bean configuration, a document also generates a default config item for a class if you don't define one yourself. The default configuration consists of just an element with the name of the class that a default configuration is being created for (the name is converted to lower-case words with hyphens like bean property names). Inside this element the default XML view of the object is inserted.

You can write your own document config if you don't like the default one. The config format is just an XML template, like the bean config format, with the proxy command node being <document:content>. Here is an example that replaces the <phone-book> root element with <book>:

<document:root config:category="test.phonebook.PhoneBook">
    <book>
        <document:content/>
    </book>
</document:root>

You can also specify a config name in a "document:name" category. When you programmatically create a JXV document view (or reader) you can specify an optional name parameter, which will be used to chose the particular document config you want. Run JXVTester with config8.xml to see the effects of this configuration item.

We have seen all the basic types of configurations available in JXV. In the next section we will cover some more advanced topics: integrating namespaces, overriding the bean property configuration generation, and some other advanced examples. You can go on reading these topics, and they would certainly enable you to use JXV more flexibly and efficiently. However, they are not mandatory for writing most applications. They may come in handy, but you can do without them. So if you are in a hurry, feel free to skip the next configuration sections and come back to it later.

Namespaces

In this section we will learn how to incorporate namespaces into JXV's XML views. Namespaces generally interact smoothly with the notion of templates. All you have to do in order to create an element with some namespace is make the template element have that namespace. However, there are a couple of questions: who creates the actual namespace declaration? how do the default configuration mechanisms interact with namespaces? how do template command nodes create attributes and elements with namespaces? This section addresses these questions and, as usual, demonstrates a configuration file using the new techniques.

JXV doesn't create namespace declaration attributes on it's own. You need to declare them, before you use them, as normal attributes in a template. That way the resulting XML view would also contain the declarations.
The default configuration mechanisms generally follow a simple inheritance scheme: elements that are generated by default would have the same prefix and namespace as their parent node. <bean:apply-defaults> allows to to override this behavior by specifying a "prefix" attribute that would determine which prefix would be used for the generated elements. Attributes are generally generated with no namespace, although you can override this.
The template command nodes <template:attr> and <template:element> require special treatment because their namespace allways has to be the template namespace. In <template:attr>, you can simply specify a QName as the value of the name attribute: that is, the name of the attribute can contain a namespace prefix. In the <template:element> case this is impossible because the name is deduced at runtime. Therefore you may specify a "prefix" attribute in the <name> element specifying a namespace prefix.

In the following configuration (based on config8.xml) we have made two changes:

  1. The default namespace declared in the <jxv-config> element is now "www.phonebook.org". We also declare it in the <book> element in the document configuration so it would be included in the XML view. This simple change has moved our entire XML view to the "www.phonebook.org" namespace. All the elements we did not configure explicitly are generated by default using their parent's namespace, and since the document root and all the explicitly configured elements have the phonebook namespace, the parent's namespace would always be the phonebook namespace.
  2. We have taken the work-address and home-address elements in the Person view and put them both in an "addresses" element. The addresses element declares a "map" namespace, and the <bean:apply-defaults> elements generating the two addresses have a "prefix" attribute set to "map". The result is that the "home-address" and "work-address" elements, along with all their children, use the map namespace.

You can read the entire configuration in config9.xml. Run JXVTester on it and observe the results.

Generating elements with runtime-determined names (aka "dynamic elements")

This section is an example of using the <template:element> command node. We will replace our current map configuration with a configuration where each map entry creates an element with the name of the location, whose children are the view of the value. Here is the relevant part (this is only the part that configures the phoneNumbers property: for the entire configuration se config10.xml):

<phone-numbers>
    <bean:property name="phoneNumbers">
        <map:root>
            <template:element>
                <name>
                    <map:key>
                        <reader:root substitute-reads="java.lang.String"/>
                    </map:key>
                </name>
                <content>
                    <map:value>
                        <reader:root substitute-reads="test.phonebook.PhoneNumber"/>
                    </map:value>
                </content>
            </template:element>
        </map:root>
    </bean:property>
</phone-numbers>

Run JXVTester with config10.xml and see how now we have elements with names matching the type of location the map entry represents.
Most of this new configuration is pretty simple. However, there is one important thing to notice. We had to enclose the property in a <phone-numbers> element, otherwise reading the XML view would fail. If the after reading all the phone numbers JXV would get to the "first-name" element, it won't know that this is not another element with a runtime-determined name. After all, we could have a key in the hashmap whose value is "first-name". So if you have a context where you have a variable number of "dynamic" elements, you have to give JXV some means for detecting when they end. If you have just one (or any statically defined number of) dynamic element this problem does not arise because JXV simply reads that many elements.

The Bean Factory's default property configuration mechanism

We have seen in the previous sections that when there is no explicit configuration given for some property, the bean factory generates one. The mechanism that generates properties and also be invoked directly through <bean:apply-defaults>. In the section we will se how the default configurations are actually generated, and how you can change the way JXV generates default configurations.

Default configurations are created by running an XSLT template on an XML document of the following format:

<property>
    <name>The name of the property</name>
    <formatted-name>The name of the property, formatted for XML output (e.g. propertyName turns into property-name)</formatted-name>
    <name-noun-detected>True or false, indicating whether the original name was in plural form and JXV found the noun</name-noun-detected>
    <name-noun>If name-noun-detected is true, this is the noun. Otherwise this is empty</name-noun>
    <prefix>A namespace prefix</prefix>
    <coloned-prefix>If the prefix is non empty, this is "prefix:". Otherwise this is empty</coloned-prefix>
    <namespace>The namespace URI</namespace>
    <property-type>The type of the property (i.e. class name)</property-type>
</property>

The name-noun and name-noun-detected elements are designed for use by container configurations. They allow the container configuration template to display each item in a "contacts" collection as "contact".
The prefix and namespace elements specify the prefix and namespace that the template should use in the XML it generates. The coloned-prefix element is merely a convenience. You will see how it is used in the next example.

To define a template, you create a config item with the "beandef" (i.e., bean default) namespace. The category of the item is matched against the property's type. The template should have one child which is the root element of an XSLT template. When generating a default configuration for a property, JXV would create a document with the format above and feed it to your template. Like in JXV's XML templates, the root element of the result is actually ignored. It merely used as a holder for attributes and a list of elements. The attributes of the root element of the result are copied to the bean configuration, and the children of the root element of the result are inserted into the bean configuration, where the <bean:apply-templates> element appeared. If the <bean:apply-templates> element generates more than one property configuration, the process described above is repeated for each property, in alphabetical order.
There is one more thing. When you use namespaces like "bean" and "container" in your XSLT templates, the XSLT processor adds namespace declarations for these namespaces in the root element. To avoid getting these namespace declarations copied into the XML view, you can define a list of "excluded-prefixes" in the config item. The list is a space-separated list of prefixes. All the namespace declarations for prefixes that appear in the list are not copied to the XML view.

A note about primitives:
I've mentioned in a note before that when you create config items for view factories and you want them to apply to primitives, you should make them apply to the primitive wrapper class instead, because JXV always creates views for objects, it never accesses primitives directly.
In the case of the beandef config item, this is not the case. Here the actual type of the property is used, and that type may very well be a primitive type. Keep that in mind when writing your own beandef templates.

Before we write our own template, let's see an example from JXV's standard configuration files:

<!-- Default configuration for collections and arrays: if the name of the property
        enabled us to get the name noun, we create an element by that name for each
        item. Otherwise, we create an element with the name of the property and put the
        default view for containers there. -->
<beandef:root config:category="subclass(java.util.Collection) | array(all)" exclude-prefixes="bean container">
    <root xsl:version="1.0">
        <xsl:choose>
            <xsl:when test="property/name-noun-detected = 'true'">
                <bean:property name="{property/name}">
                    <container:root>
                        <xsl:element name="{/property/coloned-prefix}{/property/name-noun}"
                                           namespace="{/property/namespace}">
                            <container:item/>
                        </xsl:element>
                    </container:root>
                </bean:property>
            </xsl:when>
            <xsl:otherwise>
                <xsl:element name="{/property/coloned-prefix}{/property/formatted-name}"
                                   namespace="{/property/namespace}">
                    <bean:property name="{property/name}" />
                </xsl:element>
            </xsl:otherwise>
        </xsl:choose>
    </root>
</beandef:root>

Let's go over what this template does. If the name-noun-detected element is true, it generates a child element of this format:

<bean:property name="propertyName">
    <container:root>
        <name-noun>
            <container:item>
        </name-noun>
    </container:root>
</bean:property>

Where name-noun is the noun form of the name constructed by JXV. We know from the container configuration section that this configuration would create for each item in the container a "name-noun" element containing the view of the item.
The other case is simpler. If the name-noun was not detected, the child element has this format:

<formatted-name>
    <bean:property name="propertyName"/>
</formatted-name>

This creates an element with the formatted name of the property, containing the view of the property. The view would be formatted using the default configuration: an "item" element for each item in the collection (with the parent's namespace and prefix).

Now we are ready to write our own template. We will write a template that formats all String properties as attributes instead of elements. Here is the relevant part of the configuration file:

<beandef:root xmlns="" config:category="java.lang.String" excluded-prefixes="bean template">
    <root xsl:version="1.0">
        <template:attr name="{/property/formatted-name}">
            <bean:property name="{/property/name}"/>
        </template:attr>
    </root>
</beandef:root>

Run JXVTester with config11.xml and examine the results. Notice that here we are generating attributes without namespaces prefixes. This is quite standard in many XML formats. Elements have namespaces but their attributes do not. Anyway, if you want to generate attributes with namespace prefixes all you need to do is replace <template:attr name="{/property/formatted-name}"> with <template:attr name="{/property/coloned-prefix}{/property/formatted-name}" namespace="{/property/namespace}">.
Notice the xmlns="" namespace declaration we have in the beandef config item. If we didn't have it, the <root> element would have the default namespace, which is "www.phonebook.org". That would cause the XSLT processor to add a declaration for this default namespace in the result, and that declaration would have been copied (as an attribute of the root element) to the <map:work-address> and <map:home-address> elements. This doesn't really cause any harm, it just re-declares a namespace that is already declared in the scope. Even so, I think it's better to avoid such redundant declarations.

In the paragraph above we have seen how the XSLT processor might produce namespace declarations that we didn't intend for it to create. Another case where this happens is when your template generates elements or attributes with the namespace specified in /property/namespace. You can't specify the prefix in the exclude-prefixes attribute, because the prefix itself is determined at runtime. JXV solves this by removing namespace declarations of the default namespace with the default prefix. So you don't have to worry about getting rid of these declarations.

There are similar mechanisms for generating the default configurations of proxy command nodes and some other types of configurations. However, because you only need to change those in very rare cases, they are not described in this tutorial at this point. If you still want to learn about these mechanisms, please consult the mailings lists or forums in JXV's website.

That's it for the PhoneBook example. I highly recommend you try to modify the configuration files and/or the code and see what happens. If you come across something you can't figure out, drop a message in the website. Here are some ideas for further improvement:

 

Using JXV in your code

The JXVTester tool is nice for practice, but if you want to actually use JXV in your own applications you have to know how to call it from your own code. It may seem weird that this section appears so late in the tutorial. After all, using JXV in your own applications is what this tutorial is all about, right? Fortunately, there isn't a lot to learn here, at least unless you're planning to write your own view factories or extend JXV. After all, most of what JXV provides is just implementations of standard APIs like DOM and SAX. If I had to explain all of these APIs here it would take a while. But given that they are standard and you probably know them already, all you have to learn is how to "hook" JXV into your code. In all the examples below you should import the relevant classes from org.jxv.document and org.jxv.config.

A JXV configuration is represented by the org.jxv.config.JXVConfig class. You can load the "system config", the config based on the org.jxv.config.config-files environment variable, using the JXVConfig.getSystemConfig() method.

Your main entry point into JXV would usually be the org.jxv.document package. This package provides classes for creating DOM documents, SAX sources, and XML readers for your objects. A few simple examples follow. In these examples I only pass an "object" parameter, but the methods and constructors are overloaded and can also take a JXVConfig instance (if omitted they use the system config) and a config name (if omitted they use the default name, an empty string).

To get a Document for a specified object use the following line:
Document doc = new DocumentDOMView(object);

All the nodes in a DOM document created by JXV implement the org.jxv.dom.JXVNode interface. This interface has two method for getting the contained view and the owner view of a node. From the view you can get the associated object, and thus map a node back to it's object. The test.xpath package contains a command-line utility called XPathRunner which takes an ObjectFactory (like JXVTester), creates an object, and allows you to run XPath queries on it and get back objects. This example makes use of the XPathAPI class in the "org.apache.xpath" package. Converting it to use your favorite XPath engine should be quite simple.

To get a SAXSource instance use:
SAXSource src = DocumentSAXView.getInstance(object.getClass()).getSAXSource(object);
First, we get a document SAX view for the desired class. SAX views, unlike DOM views, are instaciated once-per-class because of their stateless nature. To get a SAX source containing the the view of a specific object, we use the getSAXSource(object) method. SAXSource is a standard interface defined in the Java API for XML Processing. See the JAXP documentation for information on how to use it.
Note that a SAX view (the result of  DocumentSAXView.getInstance(object.getClass())) supports multithreading.

Finally, to read object views for object of a specified class use:
reader = new DocumentReader(clazz);
return org.jxv.reader.JXVReaderHelper.read(myInputSource, reader);

Note that of all the objects we create in this section, readers are the most "heavyweight". A reader is not multi-threaded, but it can be reused. If you plan to read more than one XML file, you should strongly consider caching the reader.

You can look at the source of org.jxv.test.JXVTester and org.jxv.test.JXVConsoleReader for working examples of this code. The "Boolean Formula Satisfier" example briefly described below demonstrates the manual creation of JXVConfig instances.

The Boolean Formula Satisfier example

This example focuses mostly on the concepts you have seen in the substitute-reads section. It reads boolean formula expressions to from XML files, and tries to find some assignment of variables that makes the expression true. The main focus is on using substitute reads to read the expression fields (which usually have the type BooleanExpression, the superclass of all expression classes).

The other thing we see in this example is how to instanciate a JXV configuration object and load definitions into it. In the PhoneBook example we used JXVTester, which uses the system configuration. However, sometimes you may want to create your own configuration instance and load definitions into it yourself rather than rely on the "org.jxv.config.config-files" environment variable. For instance, in an EJB application or Servlet you may not feel comfortable playing with the start-up scripts of your application server to pass the environment variable. Also, there is some level of mutual-blocking between multiple threads using the same JXVConfig (although it is kept to a minimum). If you discover that lock congestion is slowing down your application, you might want to load several JXVConfig instances and use a different one for each thread. Note however that these instances do tend to be somewhat heavy, because JXV does a lot of caching at various levels to improve performance.

Here is the relevant piece of code from Satisfier:

JXVConfig jxvConfig = new JXVConfig();
jxvConfig.getBaseContext().importXMLConfig("/test/bool/config.xml");

Once you instanciate a JXVConfig instance, there are two common things you can do with it:

  1. Import a configuration file using importXMLConfig(...). Several types of parameter are allowed, including a string location. The location is looked up in the classpath (of the classloader that loaded JXV), and if it does not match it is looked up in the filesystem.
  2. Load the user-defined default configuration (from the config-files environment variable) using loadUserConfig().

Note that in order two perform these two operations you first need to access the base context using getBaseContext(). This is the actual implementation of a config context (e.g., it is also used for config contexts of proxy command nodes).

There are some other options, you can look them up in the API. A bit further down the code you can see how this configuration object can be passed to the document reader class as an additional argument to the constructor.

Because this example builds it's own configuration object, you don't have to pass it any environment variables. Run this example on the sample expressions in the "samples" folder like this:

java -classpath .;../lib/jxv.jar test.bool.Satisfier test/bool/exp<X>.xml

The output is a variable assignment that satisfies the expression, or a message stating that the expression is unsatisfiable. Note that exp3.xml is intended to be malformed, the exception that is thrown is not a bug.

The Order example

This is another big, fully configured example. The classes and config files are available in the test.order package in the samples folder. If you want some more practice, trying reading the configuration files. They are documented with comments inside the XML to make this easier.

The Sales example

This example is the last, and also the simplest. In fact, it doesn't use any configuration files at all. I included it in the distribution mainly to remind you that despite all of the elaborate configuration options shown in this tutorial, JXV doesn't really require any configuration to work. Run this example using JXVTester like this (from the samples folder):

java -classpath .;../lib/jxv.jar org.jxv.test.JXVTester -rw c:\temp test.sales.SalesFactory 1 5 5

The resulting XML is exactly what you would expect. If you don't have any specific XML format in mind, this XML view is probably as good as any.

Summary

This tutorial walked you through the important aspects of JXV. I hope it provided a solid introduction, at least for those users who want to use JXV  (rather than extend it). While future versions of this tutorial may cover other features, you don't have to wait. The best way to truly understand JXV is to read the sources. They are freely available and come with every distribution.

I would be glad to hear your experiences with JXV in general and this tutorial in particular. The homepage at http://jxv.sourceforge.net contains links to mailing lists and other contacts where you can submit your comments. All comments are appreciated.

 

Appendix: Class Categories

Class categories are used in the JXV configuration system to denote the sets of classes a particular config item applies to. In order to allow you to flexibly and easily type in these categories, JXV implements an expression language for describing sets of classes. The components of this expression language are:

Parentheses can be used in expressions for grouping. Whitespaces are ignored. Any category may be preceded by a string of the from "#id#". This associates the category with the specified id, so it can be addressed using ref(id). Any identifier with a form different than the ones above is assumed be be a class name, and denotes the class itself.

Here are some examples of class category expressions, and their meaning:
a.** & ~(a.b.*): meaning any class in a sub-package of a, except for classes in package a.b.
array(subclass(java.util.Collection | java.util.Map)): an array of classes that are subclasses of Collection or Map.
#array-id#(java.util.Collection | array(ref(array-id))): java.util.Collection, java.util.Collection[], java.util.Collection[][], ...

The grammar of the expression language is as follows:

and-category:   or-category [& and-category]
or-category:     not-category [| or-category]
not-category:    [~] id-category
id-category:      ['#' id '#'] atom-category
atom-category: 'interface' 'abstract' | 'primitive' | 'subclass(' category ')' | package-name '.**' | package-name '.*' | class-name | 'all' |
                        'array(' category ')' | 'component(' category ')' | '(' category ')' | 'ref(' id ')'