Chapter 1. Getting started with XFDU API

1. Introduction

XFDU API Library conceptually consists of two layers. The first layer is low level API representing each structure in XFDU schema. The second layer is more high level API. It aggregates parts of functionality from the first layer; thus, allowing easier access to constructing and manipulating an XFDU package.Classes and interfaces that belong to lower layer reside in xfdu.core.* packages. The higher layer classes reside in xfdu.packaging.* packages. Both layers can be used either individually or in combination to manipulate an XFDU package. The higher layer API doesn't provide the functionality to fully manipulate an XFDU package. This means that while it can be used for example, to create and populate a package with major pieces of information, one still needs to invoke lower layer's methods to deal with numerous optional attributes of XFDU elements. It is expected, that coverage of the higher layer will grow over time to cover more areas of XFDU packaging. Figure 1.1 shows main classes of xfdu.packaging.* packages.

Figure 1.1. XFDU High Level API classes

XFDU High Level API classes

2. How to manipulate an XFDU package

Creating new package

Package xfduPackage  = new Package();

This will create instance of XFDU package with basic XFDU structures setup. Specifically, a new XFDU manifest will be created (instance of XFDUType element). A Package header (instance of PackageHeaderType) will be created and populated with the environment information. An Information Package Map (instance of InformationPackageMapType) will be created as well.

Adding major package elements

The Package class contains number of addXXX methods that allow addition of certain package elements to the package:

add(Data data)
add(DataObjectType data, Transformation transformation)
add(ContentInformation cu)
add(Collection metadataObjects, Collection dataObjects)
add(MetadataObjectType meta)
add(Metadata meta)

Data, Metadata, Transformation and ContentInformation classes are high level wrappers around lower level XFDU package element classes. Invoking appropriate constructors on those classes will create instances of the classes, and will setup basic XFDU structure for that element. For example, to create a Metadata object one could do this:

Metadata meta = new Metadata(MetadataObjectType.MetadataClassification.OTHER,
                             MetadataObjectType.MetadataCategory.OTHER,
                             "OTHER",
                             MimeType.TEXT_XML,
                             "file:mymetadata.xml",
                             LocatorTypeType.URL,
                             true);

In this example, it is worth mentioning meaning of last parameter of the constructor. The last parameter specifies whether the specified metadata file should be left as reference inside of XFDU manifest, or should be base64 encoded and included right in the manifest. In this case it is specified as 'true', which means the file will be encoded right into manifest.

On the other hand, metadata maybe added to the package via call to add(MetadataObjectType). However, in this case more coding is involved using lower layer classes:

XFDUElementBuilder builder = XFDUElementBuilder.GetInstance();
MetadataWrap mdWrap = builder.buildMetadataWrap(?OTHER?,"somemetadata".getBytes());
DataObjectPtrType dataObjectPtr = builder.buildDataObjectEntryPointer()
MetadataObjectType mdsec = builder.buildMetadataObjectType(null,
                                                           mdWrap,
                                                           null,
                                                           MetadataObjectType.MetadataClassification.OTHER,
                                                           MetadataObjectType.MetadataCategory.OTHER);

It is worth mentioning that XFDUElementBuilder is a helper class that provides number of factory methods to build core (lower level) XFDU data types (as defined in XFDU schema).

Preferred way of building a package

Conceptually building of XFDU package should start with adding Content Units to the package that represent logical view of the package. Information Package Map of XFDU where Content Units are added represent logical view of the package. While XFDU API allows caller to add package elements to the package in any order one wants, it is preferable to do it in "logical" way. This means that data and metadata should be added to the package via addition of Content Units that tie respective data and metadata together. XFDU higher-level API provides methods for doing just that. Specifically, xfdu.packaging.Package class has three methods:

add(Collection, Collection)
add(ContentInformation)
add(ContentUnitType parent, ContentInformation cu)

First method will take 2 arguments: first collection of metadata and second collection of data. The passed metadata and data are meant to have some logical relationship. When the method is invoked, it will add given data and metadata to the package into data object section and metadata section respectively. The latter is done by creating appropriate data object pointers in the content unit and adding metadata IDs to appropriate attributes of IDREF type in the Content Unit (each metadata's categorization attribute will be used to make this decision). Second method does the same except the caller will need to create instance of implementation of ContentInformation interface oneself before calling the method. Third method will add new content information to the specified content information (parent) . Whenever, a content unit is added to a parent (either information package map or to another content unit) it will be added as the last child in the list of content unit children of the parent. To summarize this, here is the code snippet that demonstrates the concept:

Metadata meta = new Metadata(MetadataObjectType.MetadataClassification.DED,
                             MetadataObjectType.MetadataCategory.DMD,
                             "DC",
                             MimeType.TEXT_XML,
                             "file:../xfdu_temp/mymetadatafile.xml",
                             LocatorTypeType.URL,
                             false);

Data data = new Data(MimeType.APPLICATION_HDF_EOS,
                     "file:../xfdu_temp/mydata.hdf",
                     LocatorTypeType.URL,
                     true);
List metaList = new ArrayList();
metaList.add(meta);
List dataList = new ArrayList();
dataList.add(data);
//Create ContentInformation object that encapsulates XFDU ContentUnit.
BasicContentInformation cu = new BasicContentInformation(metaList,dataList);
//add the content information to package map and the package; when the content information is added to the
//package, all the metadata and data referred by it will be added to the package as well if it wasn't added 
//before. In other words appropriate metadataObjects and dataObjects will be respectively added to the 
//metadata and data sections of XFDU manifest
pack.add(cu);

Deleting package elements

There are number of methods in the Package class that allow deletion of major package elements:

delete(ContentUnitType cu, boolean cascade)
delete(ContentUnitType parent, ContentUnitType cu)
delete(DataObjectType data)
delete(ContentUnitType cu)
delete(MetadataObjectType meta)
deleteData(String dataId)
deleteMetaData(String metaId)

The cascade attribute in the first method indicates that delectation of all the metadata and data objects referenced by the passed content unit should be deleted from the package as well. First six methods, require the caller to have references to the object(s) being deleted (since they will be located by reference). However, last tow methods, take a unique id for the object being deleted. This id corresponds to XML ID of the element to be removed. For example, to delete metadata by its id, one would do:

xfduPackage.delete("mymetaId");

Saving package

Class Package provides number of save methods to save the XFDU package in appropriate format. Once instance of package was created (and possibly populated, it can be saved by calling one of these methods (method names express the format in which package will be saved:

saveAsGzip(java.io.OutputStream)
saveAsTar(java.io.OutputStream)
saveAsJar(java.io.OutputStream)
saveAsTarGzip(java.io.OutputStream)
saveAsZip(java.io.OutputStream)
saveAsXML(java.io.OutputStream)

So, for example to save a package in ZIP format one would do:

Package p = new Package();
//populate package with data, metadata, etc...
p.saveAsZip(new FileOutputStream("myxfdupackage.zip"));

All, but saveAsXML methods, will save package in the respective format and will include all referenced data or metadata as files. However, saveAsXML method will only create XML file representation of XFDU manifest.

Opening existing package

Package class provides number of static methods to instantiate currently existent package from a file (provided that a file is in the supported format):

OpenFromGzippedTarFile(java.io.File)
OpenFromJarFile(java.io.File)
OpenFromTarredPackage(java.io.File)
OpenFromZipFile(java.io.File)
OpenFromXMLFile(java.io.File)

So, for example to open an XFDU package from JAR file, one would do this:

Package xfduPackage = Package.OpenFromJarFile(new File("myxfdupackage.jar"));

Access to low level API via Package class

Currently, higher level API provides only so much access to XFDU structure. While more APIs are planned in that direction, it is good to know how to manipulate XFDU structures.

Package class provides several accessors to retrieve XFDU structure underneath of it:

getDataObjects()
getInfoPackageMaps()
getManifest()
getMetadataObjects()

getDataObjects, getMetadataObjects and getInfoPackageMaps() return Collections of instances of DataObjectType,MetadataObjectType and InformationPackageMapType classes respectively. Then, by invoking appropriate methods on those instance one can manipulate them. getManifestMethod returns the instance of Manifest class.Caller can call appropriate accessors in that instance to get underlying XFDU structures. Alternative, one can call getXFDUManifest method which will return instance of XFDUType. From that point on, by calling appropriate getters and setter on that instance one can manipulate all the underlying XFDU structures.

Building XFDU package using both high and low level APIs

The following code snippet shows one of many ways how to build and save an XFDU package using both high and low level APIs available in the toolkit.

XFDUElementBuilder builder = XFDUElementBuilder.GetInstance();
//Follwoing line will create new package. Specifically, new XFDU 
//manifest will be created (instance of XFDUType element).
//Package header (instance of PackageHeaderType) will be created and populated with the 
//environment information. Information Package Map (instance of InformationPackageMapType)
//will be created as well.
Package pack = new Package();

//here Metadata Section (instance of MetadataSectionType) will be created. 
//New metadata (instance of MetadataObjectType) will be added to it. 
//Last parameter is set to 'false', which means that the metadata will be referenced,
//but not included. Thus, once instance of MetadataObjectType is created, metadata file will 
//referenced within it via creation of instance of MdRefType within the instance of MetadataObjectType.
Metadata meta = new Metadata(MetadataObjectType.MetadataClassification.DED,
                             MetadataObjectType.MetadataCategory.DMD,
                             "DC",
                             MimeType.TEXT_XML,
                             "file:mymetadatafile.xml",
                             LocatorTypeType.URL,
                             false);

//here instance of DataObjectType will be created. The last parameter specifies if the data
//should be referenced of base64 encoded into XFDU manifest. Since it is set to
//'true' here, the data contained in mydata.hdf file will be base64 encoded and included 
//into the manifest
Data data = new Data(MimeType.APPLICATION_X_HDF,
                     "file:mydata.hdf",
                     LocatorTypeType.URL,
                     true);
List metaList = new ArrayList();
metaList.add(meta);
List dataList = new ArrayList();
dataList.add(data);

//Instnce of ContentUnitType is created. Its metadata reference attribute is populated
//with references to metadta contained in metaList Collection and its data object pointer
//is set to point to data
BasicContentInformation cu = new BasicContentInformation(metaList,dataList);

pack.add(cu);
pack.saveAsTarGzip(new BufferedOutputStream(new FileOutputStream("myxfdupackage.tar.gz")));

The above code snippet will produce file. Within that file there will be xfdumanifest.xml that will look like this:

<xfdu:XFDU xmlns:xfdu="http://www.ccsds.org/xfdu/2004" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.ccsds.org/xfdu/2004 xfdu.xsd">    
    <packageHeader ID="ph">
        <environmentInfo>
            <specificationVersion>1.0</specificationVersion>
            <xmlData>
                <platform>Linux2.4.22-1.2129.nptl</platform>
            </xmlData>
        </environmentInfo>        
    </packageHeader>
    <informationPackageMap>
        <xfdu:contentUnit dmdID="DMD_DED1">
            <dataObjectPointer dataObjectID="dataObject1"/>
        </xfdu:contentUnit>
    </informationPackageMap>
    <metadataSection>
        <metadataObject ID="DMD_DED1" category="DMD" classification="DED">
            <metadataReference vocabularyName="DC" mimeType="text/xml" href="file:mymetadatafile.xml" locatorType="URL"/>
        </metadataObject>
    </metadataSection>
    <dataObjectSection>
        <dataObject ID="dataObject1">
            <byteStream ID="byteStream1" mimeType="application/x-hdf" size="1">
                <fileContent>
                    <binaryData>Cg==</binaryData>
                </fileContent>
                <checksum checksumName="CRC32">32d70693</checksum>                            
            </byteStream>
        </dataObject>
    </dataObjectSection>
</xfdu:XFDU>

Finally, there is XFDUElementBuilder class that has number of buildXXX methods. Using those methods one can create basic XFDU structures in the simpler manner then going through calls to lower level APIs. For example, to create instance of XFDU metadata (MetadataObjectType) using XFDUElementBuilder, one could write such code:

XFDUElementBuilder builder = XFDUElementBuilder.newBuilder();
MetadataWrapType incMetadata = builder.buildMetadatadWrap("DC",
                                             "my metadata".getBytes());
xfdu.core.MetadataObjectType metadata = builder.buildMetadataObjectType(null,
                               incMetadata,null,
                               MetadataObjectType.MetadataClassification.DED,
                               MetadataObjectType.MetadataCategory.DMD);

Observing package change events

The Package class implements Observer pattern. Users of the Package can register their observers (classes that implement java.lang.Observer interface) with an instance of Package class by calling addObserver(Observer) method of the Package class. The registered observers will be notified of structural package changes (possibly other events in the future). It is worth noting, that if notification of observers is important, high level APIs such as described above must be used for adding and deleting package elements. Usage of lower level APIs does not produce change events.

Ability to observe changes that happen in the package maybe useful, for example, in applications that have one or more different views of the package. For instance, a tree view of a package may listen for change events in a package, and update itself whenever there is a change event generated.

Validation

High level XFDU API defines simple framework for validation of an XFDU package (Figure 1.2)

Figure 1.2. 




























Validator class is a helper class that takes an instance of a Package class and instance of a class that implements ValidationHandler. After a method is invoked, two actions may happen depending on implementation of the validation handler. If the handler decides to throw an exception during first validation failure, validation will stop there and caller will be able to take action in its exception handling block. If validation handler decides to collect all of the validation events, then a caller will have a chance to collect validation events by calling getValidationEvents method on the validation handler. This method is expected to return Collection of ValidationEvent objects.

XFDU Library provides two default validation handlers (Figure 1.2):

  • Schematron validation handler

  • External schema validation handler

  • Checksum validation handler

  • Mime type validation handler

SchematronValidationHandler handles Schematron based validation of XFDU Manifest. XFDU Toolkit includes set of default Schematron rules that will be applied during validation if SchematronValidationHandler is constructed via default constructor (no arguments). If a caller wants to provide custom Schematron rules, then one of two other constructors for SchematronValidaitonHandler class need to be used to specify location of a file containing the rules.

For example, to validate a package using provided Schematron rules, one would do:

Package pack = new Package();
//populate package.....
ValidationHandler handler = new SchematronValidationHandler();
Validator.validate(pack,handler);

Collection validationEvents = handler.getValidationEvents();
//do something with the events....

To validate a package using custom provided rules, one would do this:

Package pack = new Package();
//populate package.....
ValidationHandler handler = new SchematronValidationHandler("myschematronrules.sch");
Validator.validate(pac,handler);

Collection validationEvents = handler.getValidationEvents();
//do something with the events....

Schematron-based validation is concerned with validation of XFDU manifest beyond validation that can be provided via XML schema. By default whenever a package is saved or restored there will be validation done of XFDU manifest against XFDU XML Schema. However, there are many aspects in the XFDU manifest that cannot be validated via schema. That is where Schematron validation becomes useful.

Example of Schematron validation usage

One of the default Schematron rules included in with the XFDU toolkit checks logical validity of the package. In one of the sections above in this guide, it was explained that logically valid package should have at least one Content Unit for every metadata and data added to the package. In other words, every metadata and data in the package should have a Content Unit that points to them. This is a constraint that cannot really be enforced via XML Schema validation; however, it is an important concept of XFDU. Thus, one of the default Schematron rules included with the XFDU toolkit checks this logical validity of the package. Currently the XFDU toolkit includes these Schematron rules:

<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://www.ascc.net/xml/schematron">
 <sch:pattern name="Check structure">
  <sch:rule context="dataObject">ich 
   <sch:report test="count(//xfdu:contentUnit)  &lt; 1">There are should be at least one content unit if data objects are present</sch:report>			
   <sch:report test="count(//dataObjectPointer[@dataObjectID=current()/@ID]) = 0">Data object must be referenced by at least one content unit</sch:report>
  </sch:rule>
  <sch:rule context="metadataObject">
   <sch:report test="count(//xfdu:contentUnit)  &lt; 1">There are should be at least one content unit if metadata objects are present</sch:report>						
   <sch:report test="(contains(//xfdu:contentUnit/@repID,current()/@ID) and current()/@category='REP') or
                    (contains(//xfdu:contentUnit/@dmdID,current()/@ID) and current()/@category='DMD') or
                    (contains(//xfdu:contentUnit/@pdiID,current()/@ID) and current()/@category='PDI') or
                    (contains(//xfdu:contentUnit/@anyMdID,current()/@ID) and current()/@category='ANY') or
                    (contains(//xfdu:contentUnit/@anyMdID,current()/@ID) and current()/@category='OTHER')">
                    Metadata object are referenced by at least one content unit via appropriate metadata pointing attribute</sch:report>
  </sch:rule>
 </sch:pattern>
</sch:schema>

Validation via external schema

Second validation handler provided by XFDU library called ExternalContentValidator will validate any data object if it is of XML type and it has representation metadata defined for it as XML schema (it has otherCategory attribute equal to XSD). The validator will simply traverse the manifest, identify appropriate dataobject and perform validation (if schema is available).

Checksum validation

Checksum validation handler can be used to recalculate checksums on all the files physically included in the package (as data objects in XFDU manifest) after package opening and compare recalculated values with the ones recorded in XFDU manifest for the respective data objects. If their values don't match validation event is generated for each non-matching case. Note, that checksum can be recalculated only for data objects which have checksum of supported type.

Mime type validation

Mime type validation handler can be used to identify mime types off all the files physically included in the package after package opening and compare identified mime types with the ones recorded in XFDU manifest for the respective data objects. If their values don't match validation event is generated for each non-matching case. Mime type validation handler uses JHove-based mime type recognitition. Thus, mime type identification is possible only for files that have mime type recognizable by JHove. If a file has some other mime type not known to JHove, no action will be taken.

Extensibility of validation framework

The validation API does not limit validation of XFDU package via checking integrity of its manifest. Users of the API can easily provide other kinds of validating mechanisms simply by implementing ValidationHandler interface.

Transformations

XFDU specification defines notion of transformation. Whenever an XFDU package is being created it is possible that the data being included into the package has been transformed by a certain transformation process. For example, the data could have been encrypted or compresses before hand. Currently XFDU specification defines (via XFDU manifest XML Schema) three types of transformations:

  • compression

  • encryption

  • authentication (signing)

A data object is allowed to have n number of transformations. Producer of a package is responsible for specifying in the XFDU manifest for the package each transformation that has been performed on the data object if it is desired for the receiver of the package to be able to untransform the data object to its original form. Also, it is important for the producer to specify the order of transformations, so that untransformation process is done in exactly reverse sequence. The receiver of the package using information provided in the each transformation object can then apply reverse transformation on the data object in the reverse specified order. The receiver could either manually apply each algorithm, or use XFDU toolkit which has provisions for doing so.

Reverse transformation framework and its usage

XFDU Toolkit transformation framework defines several interfaces, factory for instantiation of transformers, configuration mechanism for usage of custom transformers as well as default implementations for the some of the transformation types (Figure 1.4)

Figure 1.3. Transformation framework class diagram

Transformation framework class diagram


































































In order for a class to be a transformer that is part of this framework it needs to implement Transformer interface. The framework uses factory pattern to instantiate a transformer for a given TransfromationType (defined by XFDU XML schema). Whenever, a call to TransformerFactory.getTransformer method is made, first fully qualified transformer class name is looked up in the configuration file for the given TransformType. If such configuration exists in the configuration file, it will be used to instantiate the specified transformer. Otherwise, the default transformer for the type of transform will be used. Currently, as seen on Figure 1.4, toolkit implements default Compression and Encryption transformers. Transformer's untransform method calls for two arguments: instances of XFDUStreamSource and XFDUStreamResult respectively. These are interface that define encapsulation mechanism for source and result of a transformation. The toolkit offers default implementations for both. Finally, TransformManager class is a helper class for invoking all the transformations on the given data object.

The following demonstrates the usage of TransformManager class:

for (Iterator it = pack.getDataObjects().iterator(); it.hasNext();)
{
 DataObjectType nextDO = (DataObjectType) it.next();
 TransformManager.applyUntransformations(nextDO,new FileOutputStream("/tmp/xfdutest_untr"));
}

This listing demonstrates more closely usage of transformation API. Lets assume that an XFDU package was produced by a producer. The producer applied certain transformations to one of the data objects. When a receiver gets a package, one can do the following to reverse the transformations:

DataObjectType dataObject = pack.getDataObjectSection().get(0);
List transformList = dataObject.getTransformObject();
//Get input stream to the bytes contained within the data object
InputStream in = getInputStream(dataObject);
File tempOutputFile = null;
//Use reverse order of transformations
for (int i = transformList.size()-1; i >=0; i--)
{
 //create temp file where results of each untransformation will go
 tempOutputFile = File.createTempFile("xfdu","tmp");
 tempOutputFile.deleteOnExit();
 BufferedOutputStream tempOutStream = new BufferedOutputStream (new FileOutputStream(tempOutputFile));
 //get next transformation
 TransformObjectType nextTransformation = (TransformObjectType) transformList.get(i);
 //lookup appropriate transformer
 ReverseTransformer transformer = TransformerFactory.getTransformer(nextTransformation.getTransformType());
 //untransform, the result is written to the temp file
 transformer.untransform(new TransformationSource(in,nextTransformation), new TransformationResult(tempOutStream));
 in.close();
 tempOutStream.close();
 //use content of the temp file as input for the next untransformation
 in = new BufferedInputStream(new FileInputStream(tempOutputFile));
}
//using apache commons-io
CopyUtils.copy(in,out);
in.close()

The above example makes an assumption that result of every transformation is a single item (i.e. file) that can be than fed into next untransformation as InputStream. If this is not a case, some other work of transformation source/transformation result should be used. For example, XFDU library provides DirectorySource/DirectoryResult classes that represent the case when source of result is a directory rather than one file. Of course other kinds of source/result implementations can be written as long as they implement XFDUTransformSource/XFDUTransformResult interfaces respectively.

Using custom package mechanism

As described before, xfdu.packaging.Package class offers number of methods to save a package using different formats (e.g. ZIP, JAR etc). However, a package producer may want to use other packaging mechanisms to create a package. While, the packager is free to do this in any way one wants, the toolkit offers simple API for this purposes (Figure 1.1). Any class that wants to be a packager that can be use in the toolkit, needs to implement xfdu.packaging.Packager interface. The interface mandates only two methods: save(Package, OutputStream) and open(InputStream). As can be seen from the arguments, to save a package, Packager gets an instance of a Package, and saves it to the specified OutputStream, to open a package, to open a package, a Packager just needs an InputStream where. This allows maximum flexibility in terms of how exactly a packager saves and opens a package. Finally, xfdu.packaging.Package class, has several convenience methods to use custom packager to open and save a package:

  • save(Packager, OutputStream)

  • save(OutputStream)

  • open(Packager,InputStream)

  • open(InputStream)

First and third methods, simply take an instance of xfdu.packagin.Packager interface and an OutputStream and InputStream respectively, and delegates saving and opening of a package respectively to the given packager instance. Second and fourth methods, use configuration mechanism described below (similar to its use in transformation framework) to lookup and instantiate class that implements Packager interface and needs to be instantiated. Fully qualified name of such class must be specified in the configuration and the class must be on the classpath. Currently, toolkit doesn't provide any default packager implementation; thus, if the code wants to use custom packager, but failed to configure its usage properly, an exception will be thrown.

Configuration mechanism

Currently, if no custom transformers or packagers used, toolkit doesn't require any configuration. However, if such usage is desired, then appropriate configuration has to take place. The toolkit has a configuration manager - xfdu.uitl.ConfogurationManager class (Figure ...)

Figure 1.4. Configuration Manager

Configuration Manager








The class provides two methods to retrieve properties. One takes a String which identifies property, another will also allow to provide default value, in case property couldn't be located. ConfigurationManager can be initialized in one of two ways:

  • by calling initConfig (String) method where the argument is name of main configuration file; that file will be looked for on the classpath

  • by calling initConfig() method; in this case configuration file will be looked for by examining value of 'xfdu.configFile' system property

By default, whenever a call to either of the getProperty methods is made if ConfigurationManager wasn't initialized yet, it will be (lazy initialization). Thus, if user of ConfigurationManager, wishes to initialize the configuration manager before the default initialization would take place, one has to explicitly call one of the initConfig methods.

The main configuration file looks something like this:

<?xml version="1.0" encoding="iso-8859-1" ?>
<configuration>
  <xml fileName="conf/config.xml"/>
</configuration>

In this case, the file actually refers to an xml configuration file where concrete configuration takes place. The main file may contain reference to multiple configuration file (xml and standard java key/value pair based) as well as to system properties. The order in which files listed defines the precedence. For full documentation on how to use this kind of configuration see Apache.

Finally, the 'conf/config.xml' file in this case that contains actual configuration may look like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xfdu_configuration>
 <xfdu>
  <transform>
   <auth>
   </auth>
   <enc>
    <class>xfdu.packaging.transform.EncryptionTransformer</class>
   </enc>
   <compress>
    <class>xfdu.packaging.transform.CompressionTransformer</class>
   </compress>
  </transform>
  <packager>
  </packager>
 </xfdu>
</xfdu_configuration>  

In this example, dully qualified names of classes are specified for encryption and compression transformers. When configuration manager reads this file, the property names are constructed by concatenating names of xml elements starting with first child of the root and going to last leaf element in the given subtree. For example, to get value of fully qualified class name of encryption transformer, one has to execute this: ConfogurationManager.getProperty("xfdu.transform.enc.class");.

Users of the toolkit can add their configuration properties to such a file, or create their own configuration file(s) and refer two them from the main configuration file.

3. Addition and access of extension information in XFDU content unit via <any/> element.

The following code demonstrates how to use XFDU API to add extension information to content unit: As specified in XFDU schema, content of <extension/> element located in content unit is of XML any type (wild card). XFDU library uses JAXB to represent content of XFDU manifest. In the JAXB object tree any type is represented as instance of org.w3c.dome.Node. Thus, in order to set wild carded content of <extension/> element via XFDU API, one needs to create an instance of org.w3.dom.Node class that encapsulates desired XML information. In our case this information comes from SIP schema and can be of 3 types:

  • sipTransferObjectInformation

  • sipObjectGroup

  • sipDataobjectType

For purposes of this exercise, needed SIP information was created as templated text. Then, it was parsed in, filled with appropriate SIP lookup values and transformed into instance of org.w3c.dome.Node class. The following code snippet, assumes that instance of org.w3c.dome.Node was previously created to encapsulate appropriate SIP information:

org.w3c.dome.Node sipRelatedInformation;

Create instance of xfdu.core.ExtensionType:h

ExtensionType ex = XFDUElementBuilder.GetInstance().buildExtensionType();

Use setter method to set SIP information:

ex.setAny(sipRelatedInformation);

Use setter method to set instance of xfdu.core.ExtensionType on currently processed content unit:

ContentUnitType currentContentUnit;
currentContentUnit.setExtension(ex);

The following code example shows how to access already existing extension information:

xfdu.core.ContentUnitType currentContentUnit;

Extract content of <any/> element as instance of org.w3c.dom.Node:

org.w3c.dom.Node  extensionInformation = (org.w3c.dom.Node)currentContentUnit.getExtension().getAny();

Using DOM4J api for convenience:

org.dom4.io.DOMReader reader = new org.dom4.io.DOMReader();
org.dom4j.Document doc = reader.read(extensionInformation .getOwnerDocument());

Iterate over attributes:

for (Iterator it = doc.getRootElement().attributes().iterator();it.hasNext();)
{
 org.dom4j.Attribute nextAttr = (Attribute) it.next();
 System.out.println("\t"+nextAttr.getName()+": "+nextAttr.getValue());
}

Usage Scenario for <any/>

As example for this scenario lets take creation of XFDU with SIP extenstion via <extension/> element that needs to be put as wild-carded element in xfdu contentUnit allowed by XFDU schema. Using XFDU API, a user would do the following: Create content unit:

BasicContentInformation cu = new BasicContentInformation();

Read in XML representing SIP extension. In this case sipTransferObject:

SAXReader reader = new SAXReader();
org.dom4j.Document document = reader.read(
         ?<sip:sipTransferObject sip:transfer_object_id='ag-chem1' 
          sip:descriptor_id='usgs-ag_data_ag_chem-sdts-transfer-object'/>?);

Create XFDU instance of xfdu.core.ExtensionType that encapsulates the extension:

ExtensionType ex = XFDUElementBuilder.GetInstance().buildExtensionType();

Set the extension instanciated as org.w3c.dom.Node into instance of XFDU instance of xfdu.core.ExtensionType:

DOMWriter writer = new DOMWriter();
ex.setAny(writer.write(document).getDocumentElement());
cu.getXfduContentUnit().setExtension(ex);

When XFDU manifest serialized into XML, the result of the above operation will look like this:

 <xfdu:contentUnit>
            <extension>
                <sip:sipTransferObject sip:transfer_object_id="ag-chem1" sip:descriptor_id="usgs-ag_data_ag_chem-sdts-transfer-object"/>
            </extension>
</xfdu:contentUnit>

4. Addition and access of third party information via <anyAttribute/>

As was mentioned before, XFDU API uses JAXB to represent XFDU manifest. When an XML schema defines <anyAttribute/> on an element, JAXB represents such attributes as instance of java.util.Map. In this map, keys are instances of javax.xml.namespace.Qname class (that is fully qualified name of the attribute) and value if the value of the attribute. The following code snippet shows how to add via XFDU API:

Access information package map:

xfdu.core.InformationPackageMap infoMap = package.getInformationPackageMap();

Access map of any attributes:

java.util.Map anyAttributeMap = infoMap.getOtherAttributes();

Add new attribute to the map:

anyAttributeMap.put(new javax.xml.namespace.Qname(?sip?,?sip_id?),?isee-sip-001?);

To access existing any attribute information, one would simply iterate over the map:

for (Iterator it = p.getInfoPackageMap().getOtherAttributes().keySet().iterator(); it.hasNext(); )
{
 Object nextKey = it.next();
 System.out.println("\t"+nextKey+": "+p.getInfoPackageMap().getOtherAttributes().get(nextKey));
}

Usage Scenario for <anyAttribute/>

As example for this scenario lets take creation of XFDU with SIP extenstion via <anyAttribute/>. That some attributes from SIP namespace will be added to XFDU contentUnit as allowed by XFDU schema. Using XFDU API, a user would do the following: Create content unit:

BasicContentInformation cu = new BasicContentInformation();

Access xfdu.core.ContentUnitType:

ContentUnitType xfduCU = cu.getXfduContentUnit();

Access map of wild-carded attributes and put desired attributes in it:

xfduCU.getOtherAttributes().put(
  new Qname("sip","transfer_object_id"),"ag-chem1");
xfduCU.getOtherAttributes().put(
  new Qname("sip","descriptor_id"),
  "usgs-ag_data_ag_chem-sdts-transfer-object");

When XFDU manifest serialized into XML, the result of the above operation will look like this:

<xfdu:contentUnit sip:transfer_object_id="ag-chem1" sip:descriptor_id="usgs-ag_data_ag_chem-sdts-transfer-object"/>