You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 6 Next »

What is Aegis?

Aegis is a databinding. That is, it is a subsystem that can map Java objects to XML documents described by XML schema, and vica-versa. Aegis is designed to give useful mappings with a minimum of programmer effort, while allowing detailed control and customization.

Aegis began as part of XFire, and moved with XFire into Apache CXF.

You can use Aegis independently of CXF as a mechanism for mapping Java objects to and from XML. This page, however, describes Aegis as used inside of CXF.

Aegis has some advantages over JAXB for some applications. Some users find that it produces a more natural XML mapping for less configuration. For example, Aegis has a default setting for 'nillable', allowing you to declare it for your entire service in one place instead of having to annotate every single element. The biggest advantage of Aegis, however, is a convenient way to customize the mapping without adding (@)annotations to your Java code. This allows you to avoid class loading dependencies between your data classes and your web service binding.

Getting Started: Basic Use of Aegis

You can configure any web service to use the Aegis data binding. A service configured with Aegis will yield a valid WSDL description, and you can use that to configure any client that you like. You can talk to an Aegis service with JAXB, or .NET, or a scripted language, or ... Aegis itself.

You can use Aegis as a client to talk to Aegis, by using the very same Java classes and configuration files in the client environment that you use on the server. However, it's not all that practical to use Aegis as a client to talk to some a service using some other data binding, since Aegis lacks a 'wsdl2java' tool.

Every CXF service and client uses a front end: JAX-WS, Simple, etc. Each of these provides a place to configure the data binding, both in Spring and via Java code.

For example, here is a Simple front-end service using Aegis as a data binding.

  <simple:server id="pojoservice" serviceClass="demo.hw.server.HelloWorld" address="/hello_world">
  	<simple:serviceBean>
  		<bean class="demo.hw.server.HelloWorldImpl" />
  	</simple:serviceBean>
         <simple:dataBinding>
       <bean class="org.apache.cxf.aegis.databinding.AegisDatabinding" />
    </simple:dataBinding>
  </simple:server>
 </bean>

AegisDatabinding is the class that integrates Aegis into CXF as a databinding.

Aegis Operations - The Simple Case

How does Aegis work? Aegis maintains, for each service, a set of mappings from Java types (Class<?> objects) to XML Schema types. It uses that mapping to read and write XML. Let's look at a simple service, where all the Java types involved are either Java built-in types, other types with predefined mappings to XML Schema, or simple bean-pattern classes that have properties that (recursively) are simple.

Let's start with serializing: mapping from Java to XML. (JAXB calls this marshalling, and cannot decide how many 'l's to use in spelling it.) Given a Java object, Aegis looks to see if it has a mapping. By default, Aegis has a set of default mappings for the basic types defined in XML Schema, plus a few other special items. These mappings are implemented by Java classes, parts of Aegis, that can turn objects in to XML and vica versa. In particular, note that Aegis will map a DataSource or DataHandler to an MTOM attachment.

What if Aegis finds no mapping for a type? In the default configuration, Aegis invokes the type creators to create a mapping. Type creators use several mechanisms to create XML schema from Java objects. This include reflection, annotations, and XML type mappings files. As part of the mapping process, Aegis will assign a namespace URI based on the Java package. (Note: Aegis does not support elementForm='unqualified' at this time.) These mappings are implemented by a generic mapping class, and stored away.

How about the reverse process: deserializing? (JAXB calls this unmarshalling.) In this case, by default, Aegis is presented with an XML element and asked to produce a Java object. Recall, however, that the Aegis maintains a mapping from Java types to XML Schema Types. By default, an XML instance document offers no information as to the type of a given element. How can Aegis determine the Java type? Outside of CXF, the application would have to tell Aegis the expected type for the root element of a document. Inside CXF, however, Aegis gets the benefit of the Message and Part information for the service. The WSDL service configuration for a service gives enough information to associate an XML Schema type with each part. Once the front-end has determined the part, it can call Aegis with the QName for the schema type, and Aegis can look it up in the mapping.

Will it be in the mapping? Yes, because Aegis precreates mappings for the types in the service's parts. Aegis cannot dynamically create or choose a Java class based on XML schema, so the type creators cannot start from XML.

Using Java Classes That Aren't Visible to the Service Interface

Many web service programmers want to use types that are not directly visible by reflection of the service interface. Here are some popular examples of types that programmers want to use for property or parameter types:

  • Declare a base type, but transfer any one of a number of classes that extend it.
  • Declare a raw Collection class, such as a Set, List, or Map, and send arbitrary objects as keys and values.
  • Declare a base exception type for 'throws', and then throw other exception classes that derive from it.
  • Declare an interface or an abstract type.

Aegis can handle all of these. For all except interfaces, there are two mechanisms that involved: the root class list and xsi:type attributes.

As explained above, Aegis can write 'anything', but it can only read objects of types that are mapped. You must give Aegis a list of all the types that you want to use over and above those visible from the service, and you must instruct Aegis to send xsi:type attributes. These type attributes allow Aegis to identify the type of these additional objects and look them up in the mappings.

Interfaces require one further step. Obviously, Aegis cannot instantiate (run 'new') on an interface. So knowing that a particular XML Schema type maps to an interface is not enough information. To be able to read an XML element that corresponds to an interface, Aegis must know a 'proxy class' that implements the interface. You must give Aegis a mapping from interface types to proxy class names.

How does this work? The core of Aegis is the AegisContext class. Each AegisDatabinding object has an AegisContext. (It is probably not possible to share an AegisContext amongst databindings.)

By default, AegisDatabinding will create its own AegisContext with default properties. To configure additional types, as well control other options that we will examine later on, you must create the AegisContext for yourself and specify some of its properties. Then you pass your AegisContext object into your AegisDatabinding object.

To use additional classes or interfaces, you need to set two (or three) properties of your AegisContext.

  • rootClasses is a collection of Java Class<?> objects. These are added to the list of types known to Aegis. Aegis will create a mapping for each. For convenience, there is a rootClassNames property for use from Spring.
  • writeXsiTypes is a boolean. Set it to true to send xsi:type attributes.
  • beanImplementationMap is a mapping from Class<?> to class names. Use this to specify proxy classes for interfaces (or abstract classes).

Global Type Creation Options

There are a few global options to the default type mapping process. You can control these by creating a org.apache.cxf.aegis.type.TypeCreationOptions and passing it into your AegisContext object.

There are four properties in the class, of which two are much more commonly used.

  • defaultNillable defines the default value of the nillable attribute of xsd:element items in the xsd:sequences built for non-primitive types. By default it is true since any Java reference can be null. However, nillable='true' has annoying consequences in some wsdl2java tools (turning scalars into arrays, e.g.), and so many programmers prefer to default to false.
  • defaultMinOccurs defines the default value of the minOccurs attribute of xsd:element items in the xsd:sequences built for Java arrays. In combination with nillable, programmers often want to adjust this value from 0 to 1 to get a more useful mapping of an array.
  • defaultExtensibleElements causes each sequence to end with an xsd:any. The idea here is to allow for schema evolution; a client that has generated Java from one version of the service will tolerate data from a newer version that has additional elements. Use this feature with care; version management of web services is a complex topic, and xsd:any may have unexpected consequences.
  • defaultExtensibleAttributes causes each element to permit any attribute. By default, Aegis doesn't map any properties or parameters to attributes. As with the element case, care is called for.
  • No labels