JCX


Magnolia JCX is a module which attempts to support JAXB annotations in order to access Jackrabbit repositories. Here's an introduction into JCX: JCX

Current GAV:

<dependency>
  <groupId>com.kasisoft.mgnl</groupId>
  <artifactId>ks-mgnl-jcx</artifactId>
  <version>0.5</version>
</dependency>

Quick Tip: Checkout the test cases of the project in order to get a good impression of the capabilities.

 

Default behaviour

The following rules will be followed by the JCX module. If you encounter a discrepancy to these rules it's likely to be an unintended error, so I would appreciate to get an error report:

 

Handling of required properties must be explicitly configured. The default behaviour is that a missing but required value is only logged (subject to change !). You can update the JcxUnmarshaller accordingly but the handler should only be set once within your application:

public class MyModule implements ModuleLifecycle {
  
    private JcxUnmarshaller unmarshaller;

    @Inject
    public MyModule( JcxUnmarshaller unmarshaller ) {
        this.unmarshaller = unmarshaller;
    } 

    @Override
    public void start( ModuleLifecycleContext moduleLifecycleContext ) {
        unmarshaller.setUnsatisfiedRequireHandler( this::requireHandler );
    }

    @Override
    public void stop( ModuleLifecycleContext moduleLifecycleContext ) {
    }

    private void requireHandler( Node node, String type, String property ) {
        throw new RuntimeException( "can't access property '" + property + "' of type '" + type + "'" );
    }

} /* ENDCLASS */

XML Attributes

 

Examples

// property name: age, required: false
@XmlAttribute
private Integer    age;

// property name: gender, required: true
@XmlAttribute
private boolean   gender;

// meta property name: @uuid, required: false
@XmlAttribute(name = "@uuid")
private String   identifier;

// property name: duration, required: true
@XmlAttribute(required = true)
private Long   duration;

// property name: result, required: true
@XmlAttribute(required = true, name = "result")
private Float   wumpi;

If you want to use an adapted type you need to provide a corresponding @XmlJavaTypeAdapter . The most likely scenario are the conversions between an Enumeration and a String.

enum Position {
  left, center, right;
}

class PositionAdapter extends XmlToTypeAdapter<String, Position> {
    public PositionAdapter() {
        // see  com.kasisoft.libs.common.xml.adapters;
        super( new EnumerationAdapter<>( Position.class ) );
    }
}

@XmlAttribute
@XmlJavaTypeAdapter(PositionAdapter.class)
private Position    position;

XML Elements

The annotation @XmlElement has to be used for complex types when they're being used alone or as part of a Collection. The attribute values name and required are supported.

Each type must be capable to be created using the Guice framework which means that the type either has an empty default constructor or the parameters can be injected with values through Guice.

 

Examples

// property name: link, required: false
@XmlElement
LinkModel          link;

// property name: address, required: true
@XmlElement(required = true)
Address    address;

// property name: contacts, required: false
// NOTE: The GenericsType annotation is subject to change
@XmlElement
@GenericsType(Contact.class)
List<Contact>    contacts;

// property name: baskets, required: false
// NOTE: An XmlElementWrapper should be used if the list elements are located under a subnode which serves as a container node
@XmlElementWrapper
@XmlElement
@GenericsType(Basket.class)
List<Basket>    baskets

Extensions

The following examples provides extensions to the general mechanism:

// the dialog definition stores the UUID to contact workspace in the property 'author'.
// this annotation uses this to use the referred node as a data source for the class ContactModel
@JcxReference(value = "contacts", property = "author")
ContactModel   author;

The JcxUnmarshaller supports the following interfaces:

public interface IPostProcessor {

  /**
   * This function will be executed after properties had been loaded.
   */
  void postprocess();
  
} /* ENDINTERFACE */


public interface IContent {

  boolean hasContent();
  
} /* ENDINTERFACE */

These interfaces don't need to be implemented. Their meaning is as followed:

 

IPostProcessor

The function postprocess will be called after the JcxUnmarshaller has performed all configuration operations on an instance. It allows you to handle scenarios where model attributes are interdependent or if you want to provide derived information of model attributes.

 

IContent

The function hasContent is used to decide whether an instance has a value or not. If you return false the object is interpreted as invalid and thus dismissed.

This means that a null value is used instead or in case of a Collection it's not being added so it's safe to assume that each element in a list can be considered to be complete.

Technical Documentation

TO BE DONE