Beck maps between Java objects and XML.

See:
          Description

Packages
com.openharbor.beck The API.
com.openharbor.beck.apache.axis Integration with the Apache Axis SOAP framework.
com.openharbor.beck.configure  
com.openharbor.beck.get  
com.openharbor.beck.id  
com.openharbor.beck.map Mapping between Java and XML, and customization thereof.
com.openharbor.beck.marshal  
com.openharbor.beck.receive  
com.openharbor.beck.sax  
com.openharbor.beck.set  
com.openharbor.beck.unmarshal  
com.openharbor.beck.util  
com.openharbor.beck.write  
com.openharbor.beck.xml  
com.openharbor.beck.xpath  

 

Beck maps between Java objects and XML. The mapping is configurable (via an XML configuration file) and extensible (by adding Java software to Beck's framework).

Beck is not a binding tool.  It does not generate Java classes from XML schema, nor generate XML schema from Java classes.  Beck assumes you already have Java classes to contain your data.

Getting Started

To map from XML to Java, call a read method of an ObjectXMLReader. To map from DOM to Java, call the read method of an ObjectDOMReader. To map from Java to XML, call a write method of an ObjectXMLWriter.

To customize the mapping, pass a customized mapper object to the constructor of your ObjectXMLReader, ObjectDOMReader or ObjectXMLWriter. For most applications, we recommend you construct a DefaultMapper from XML, like this:

    DefaultMapper mapper = DefaultMapper.newInstance
                             (ObjectXMLReader.parseConfiguration
                                 (Configuration.MINIMAL_XML));
Replace Configuration.MINIMAL_XML with your mapping XML (a String). You can customize the mapping by changing your mapping XML. For directions, see Configuration.MAP_FROM_XML. It's often convenient to store the mapping XML in a file or resource, and use ObjectXMLReader.read (instead of parseConfiguration) to read it.

More ambitious customizers can construct a mapper in other ways. You can extend the DefaultMapper class, overriding some of its methods to customize its behavior, and configure the name of your new class in the mapping XML. You can hand-craft a Configuration class, from which you construct a DefaultMapper (or customized extension). Or you can eliminate the Configuration entirely, and hand-craft a class that implements MapFromXML and/or MapToXML.

DefaultMapper implements both MapToXML and MapFromXML.  DefaultMapper contains a Configuration, which can affect most of its behaviors.  DefaultMapper also contains a copy of the configuration parameters that it has used, in a structure optimized for speed (a map from Configuration.Selector to the resulting value).

Mapping to XML

Here's how the Beck software modules work together, to copy data from objects to XML.  It's helpful to understand this before you customize the software.

Beck copies data from a source object and the objects to which it refers, the objects to which they refer, etc.  As Beck traverses these objects, it copies their data through a MapToXML to an XMLWriter.  For each source object, the MapToXML chooses a Marshaller.  The Marshaller chooses an XML element or attribute and writes it to the XMLWriter.  If the source object contains child objects, they are handled in the same way (recursively), so their XML elements or attributes are contained in the XML of their parent.

A Marshaller can (and often does) delegate behaviors to its MapToXML:
MapToXML and Marshaller use a Getter to follow a reference from one object to another.  One kind of Getter calls a method to get a JavaBean property.  Other kinds of getters get a field, an array element or a collection member.  You can configure getters to access private fields or JavaBean properties.  And you can implement custom getters; for example, to call other methods, follow a chain of references or compute data.

Here are some guidelines for customization:
When implementing a custom class, extend a class that's similar.  Read the javadoc of the interfaces you implement and classes you extend.  The Beck interfaces impose some requirements on you.  For example, most objects should be immutable, to support re-entrant usage.

Beck traverses the graph of objects depth-first, using a recursive algorithm.  The traversal stack is the method call stack.  The traversal state is not contained in the MapToXML, Marshaller or Getter objects.  Many of those objects are singletons, and it's common for a single instance (e.g. a single Marshaller) to be in simultaneous (re-entrant) use at several levels of a traversal stack.

Beck also traverses the tree of XML elements and attributes.  There's another stack for this, in XMLWriter.  You can put state in this stack, by constructing a MapKey and using it to access your own private element of each stack frame.

A MapToXML is not required to be thread-safe, and DefaultMapper is not thread-safe.  (Its configuration parameter cache is not synchronized.)  So don't allow concurrent threads to use a single MapToXML.  A simple way to avoid concurrent usage is to construct a new MapToXML each time you copy an object to XML.  But you'll get better performance if you re-use a DefaultMapper (because its cache is warm).  So, you might want to allocate mappers from a thread-safe pool, such as a DefaultMapperPool.

Mapping from XML

Here's how the Beck software modules work together, to copy data from XML to objects.  It's helpful to understand this before you customize the software.

Beck copies data from XML via a MapFromXML to new Java objects.

Beck receives XML in the form of an event stream; that is a sequence of calls to methods of an XMLReceiver chosen by the MapFromXML.  Often these come from a SAX2 event stream via SAXContentHandler.  To read from an XML document, use a SAX2 parser (such as Xerces or Piccolo).  To read from a DOM tree, use ObjectDOMReader.copy.  A less efficient alternative is to serialize the DOM tree to XML and parse the XML with a SAX2 parser.

For each XML element, the MapFromXML allocates an Unmarshaller, which usually constructs an object and copies the data into it.  To construct an object, an Unmarshaller usually delegates to its MapFromXML, which usually delegates to an ObjectFactory, which is configurable.

The Unmarshaller class for a given situation is configurable.  If it's not configured, the DefaultMapper tries to determine the target object class (or base class or interface), and choose an Unmarshaller appropriate for that class.  The object class can be configured, or chosen by the parent Unmarshaller (e.g. the class of a JavaBean property or mapped from the name of the source element or attribute (by a Namer).

Beck traverses XML elements and attributes in XML document order (that is the sequence of events passed to the XMLReceiver).  The traversal stack is DefaultXMLReceiver.unmarshallerStack.  Each Unmarshaller has a reference to the Unmarshaller one deeper in the traversal stack (that is its 'parent').

After traversing an XML element (and its contained elements), DefaultMapper passes that element's Unmarshaller to its parent's addChild method.  The parent usually creates a reference to the child's object; for example, the parent may set a JavaBean property that refers to the child object, or add the child object to a collection.

An Unmarshaller is not re-entrant; it handles only one XML source at a time.  But an Unmarshaller is serially re-usable; that is, a given Unmarshaller will be used to handle multiple XML sources that are not nested.  The Unmarshaller pools are kept in DefaultMapper.unmarshallerClass2Pool.

Here are some guidelines for customization:
I can't think of any reason to customize the XMLReceiver.  If you want to implement a systematic XML transformation, you're probably better off implementing a filter whose output is a SAX2 event stream.  If you don't want to supply a SAX2 event stream, you can call the Unmarshaller interface instead.  But it's not much different.

Non-public Java

Ordinarily, Beck accesses properties of Java objects using JavaBean style methods getProperty() or setProperty(newValue).  If no such method is available, Beck will access a Java field directly, assuming that the field name is the property name.  Non-public properties can be copied from XML.  Beck will call setter methods or mutate fields regardless of whether they're public, private, protected or package access.  You can prevent this (if it's a problem) by overriding DefaultMapper.findDefaultSetters or findNonPublicSetters.  Beck copies only public properties to XML, by default.  That is, by default Beck won't call non-public getProperty methods, nor read from non-public fields.  To copy from a non-public property, use a mapping configuration file element like <choose includeProperties="privateProperty protectedProperty"/>.  Properties that are explicitly included in this way will be copied to XML regardless of whether they're public.  As usual for Java software, Beck can't access non-public classes unless they're declared in the same package as the accessor.  If you need to copy to or from a non-public class, we recommend you create a class in the same package that extends a Beck class, and configure Beck to use your new class.  Your extension class can access the non-public class (because it's in the same package) but inherit most of its behavior from Beck.  An alternative is to declare the non-public class in the same package as Beck.  But we recommend you don't do this: it surprises people, and risks a name conflict. 

XML Schema

Beck has rudimentary support for mapping to XML using XML schema.  Beck uses the schema to choose the namespace of XML elements and attributes (if they aren't specified by the mapping), to select which attributes and elements are (and are not) output, and to determine the order of output elements.  This is fairly crude.  It's not guaranteed to produce XML that conforms to the schema.

Cross References

Back can support cross references within an XML document, if you configure DefaultMapper to use an Identifier implementation.  When copying from Java to XML, the first reference to a given object maps to the complete XML data, and subsequent references map to a brief element that refers to the first.  When copying from XML to Java, the structure is re-created, with multiple Java references to a single object.

The code that maps from XML is fairly complicated.  It stores dangling references and potential referents in the documentState of the Unmarshaller.  Look for the code that refers to Identifier.IDREF_TO_DANGLING_REFERENCES and IDREF_TO_REFERENT.