Difference between revisions of "Metadata Broker"

From Gcube Wiki
Jump to: navigation, search
(Usage Examples)
(Transformation Programs)
Line 15: Line 15:
 
* One or more transformation rule definitions.
 
* One or more transformation rule definitions.
  
Note: The name of the input or output schema must be given in the format '''NAME=URI''', where NAME is the name of the schema and URI is the URI of its definition, e.g. '''<nowiki>DC=http://dublincore.org/schemas/xmls/simpledc20021212.xsd</nowiki>'''.
+
'''Note''': The name of the input or output schema must be given in the format '''NAME=URI''', where NAME is the name of the schema and URI is the URI of its definition, e.g. '''<nowiki>DC=http://dublincore.org/schemas/xmls/simpledc20021212.xsd</nowiki>'''.
  
 
==== Transformation Rules ====
 
==== Transformation Rules ====

Revision as of 11:40, 16 July 2007

Metadata Broker

Introduction

The main functionality of the Metadata Broker is to convert XML documents from some input schema and/or language to another. The inputs and outputs of the transformation process can be single records, ResultSets or entire collections. In the special case where both the inputs and the output are collections, a persistent transformation is possible, meaning that whenever there is a change in the input collection(s), the new data will be automatically transformed in order for the change to be reflected to the output collection.

Transformation Programs

Complex transformation processes are described by transformation programs, which are XML documents. Transformation programs are stored in the DIS. Each transformation program can reference other transformation programs and use them as “black-box” components in the transformation process it defines.

Each transformation program consists of:

  • One or more input definitions. Each one may be a:
    • Data input: accepts a reference to a ResultSet, Collection or Record to be transformed. It also contains the schema-id and language-id that the input data should have.
    • Input variable: a placeholder for an additional string value which must be passed to the transformation program at run-time.
  • Exactly one output definition, which contains the output data type (Record, ResultSet or collection), schema and language.
  • One or more transformation rule definitions.

Note: The name of the input or output schema must be given in the format NAME=URI, where NAME is the name of the schema and URI is the URI of its definition, e.g. DC=http://dublincore.org/schemas/xmls/simpledc20021212.xsd.

Transformation Rules

Transformation rules are the building block of transformation programs. Each transformation program always contains at least one transformation rule. Transformation rules describe simple transformations and execute in the order in which they are defined inside the transformation program. Usually the output of a transformation rule is the input of the next one. So, a transformation program can be thought of as a chain of transformation rules which work together in order to perform the complex transformation defined by the whole transformation program.

Each transformation rule consists of:

  • One or more inputs definitions. Each definition contains the schema, language, type (record, ResultSet, collection or variable)and data reference of the input it describes. Each one of these elements (except for the 'type' element) can be either a literal value, or a reference to another value defined inside the transformation program (using XPath syntax).
  • Exactly one output, which can be:
    • A definition that contains the output data type (Record, ResultSet or collection), schema and language.
    • A reference to the transformation program‘s output (using XPath syntax). This is the way to express that the output of this transformation rule will also be the output of the whole transformation program, so such a reference is only valid for the transformation program‘s final rule.
  • The name of the underlying program to execute in order to do the transformation, using standard 'packageName.className' syntax.

A transformation rule can also be a reference to another transformation program. This way, whole transformation programs can be used as parts of the execution of another transformation program. The reference can me made using the unique id of the transformation program being referenced and a set of value assignments to its inputs and variables.

Note: The name of the input or output schema must be given in the format NAME=URI, where NAME is the name of the schema and URI is the URI of its definition, e.g. DC=http://dublincore.org/schemas/xmls/simpledc20021212.xsd.

Programs

A program (not to be confused with transformation program) is the Java class which performs the actual transformation on the input data. A transformation rule is just a XML description of the interface (inputs and output) of a program. A program must implement the Program Java interface:

interface Program {
  public String getOutput();
}

getOutput() returns the output of the transformation program as a string. If the output is a record, the return value should be the transformed record. If the output is a ResultSet, the return value should be the ResultSet EPR. Finally, if the output is a collection, the return value should be the collection id.

The Program interface does not define any transformation methods. Each program can define any number of methods, but when the transformation rule which references it is executed, the metadata broker service will use reflection in order to locate the correct method to call based on the input and output types defined in the transformation rule that initiates the call to the program's transformation method. The valid data types for the parameters of each transformation method (so that the broker can locate and use them) are:

  • RecordType: A data type that holds the schema, language and payload of a full record.
  • ResultSetType: A data type that holds the schema, language and EPR of a ResultSet.
  • CollectionType: A data type that holds the schema, language and id of a collection.
  • VariableType: A data type that holds the string value of a variable defined inside a transformation program.

The definitions of these data types are contained in the metadata broker library.

When a transformation method of a program is called as the result of the execution of a transformation rule with N inputs and one output, the following convention is used:

  • The first N parameters passed to the method are objects holding information about the input data.
  • The last parameter is an object holding information about the output data.

The type of each parameter should one of the four types mention before (RecordType, ResultSetType, CollectionType, VariableType).

Implementation Overview

The metadata broker consists of two components:

  • The metadata broker service
    The metadata broker service provides the functionality of the metadata broker in the form of a stateless service. In the case of a persistent transformation, the service creates a WS-Resource holding information about this transformation and registers for notifications concerning changes in the input collection(s). The created resources are not published and remain completely invisible to the caller.

    The service exposes the following operations:
    • transform(TransformationProgramID, params) -> String
      This operation takes the DiligentID of a transformation program stored in the DIS and a set of transformation parameters. The referenced transformation program is executed using the provided parameters, which are just a set of value assignments to variables defined inside the transformation program. The metadata broker library contains a helper class for creating such a parameter set.
    • transformWithNewTP(TransformationProgram, params) -> String
      This operation offers the same functionality as the previous one. However, in this case the first parameter is the full XML definition of a transformation program in string format and not the DiligentID of a stored one.
    • findPossibleTransformationPrograms (InputDesc, OutputDesc) -> TransformationProgram[]
      This operation takes the description of some input format (type, language and schema) as well as the description of a desired output format, and returns an array of transformation programs definitions that could be used in order to perform the required conversion. These transformation programs may not exist before invoking this operation. They are produced on the fly, by combining all the existing transformation programs which are compatible with each other, trying to synthesize more complex transformation programs. Of course, if there is already an existing transformation program which is applicable for the requested type of transformation, it is included in the results.

  • The metadata broker library
    The metadata broker library contains the definitions of the RecordType, CollectionType, ResultSetType and VariableType Java classes, as well as the definition of the Program Java interface. The following programs are also included in it:
    • Generic XSLT record-to-record transformer (GXSLT_Rec2Rec): transforms a given record using a given XSLT definition. The output is the transformed record.
    • Generic XSLT ResultSet-to-ResultSet transformer (GXSLT_RS2RS): transforms a given ResultSet using a given XSLT definition, producing a new ResultSet. The output is the new ResultSet's EPR.
    • Generic XSLT Collection-to-Collection transformer (GXSLT_Col2Col): transforms a given collection using a given XSLT, producing a new colletion. The output is the new collection id.
    • Generic XSLT ResultSet-to-Collection transformer (GXSLT_RS2Col): transforms the records of a given ResultSet using a given XSLT, and adds them to a new collection with caller-defined attributes. The output is the new collection id.
    • Generic XSLT Collection-to-ResultSet transformer (GXSLT_Col2RS): transforms each record of a given collection using a given XSLT and creates a new ResultSet containing the transformed records. The output is the new ResultSet's EPR.
The transformation of metadata using any of the above programs, except for the GXSLT_Rec2Rec program, is a non-blocking operation. This means that the caller will not block until the transformation is completed, since the process of transforming a big ResultSet or collection may be quite time-consuming. For this purpose, each program prepares the output data (which is either the endpoint reference of the output ResultSet or the ID of the output collection, depending on the output data type of the transformation) which should be returned to the caller and then spawns a new thread to perform the transformation process.
Internally, some programs depend on others, meaning that they use other programs in order to avoid useless code duplication. For instance, the GXSLT_Rec2Rec program is used by every other program because the transformation of any complex type of data input (such as ResultSets or collections) finally comes down to transforming single records one-by-one. Of course the XSLTs are always compiled before performing bulk transformations, in order to make the whole process faster.
Each program is placed in a java package of its own, beginning with ‘org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs’. However, this is just a convention followed for the default programs contained in the metadata broker library. There is no restriction on the package names of user-defined programs. In order for user-defined programs to be accessible by the Metadata Broker, they should be put in JAR files and copied to the ‘lib’ directory under the installation directory of ws-core (or to any directory that belongs to the CLASSPATH environment variable).

Dependencies

  • MetadataBrokerService
    • jdk 1.5
    • WS-Core
    • MetadatBrokerLibrary
    • DISHLSClient
  • MetadataBrokerLibrary
    • jdk 1.5
    • WS-Core
    • ResultSet bundle
    • DISHLSClient
    • Metadata catalog service stubs
    • Metadata catalog library

Usage Examples

The following examples show how some of the transformation programs contained in the metadata broker library can be used. For this purpose, the client-side code is shown, describing the necessary steps to invoke the operations of the metadata broker service. Furthermore, the full definition of the referenced programs and transformation programs is also given. These definitions can be used as the base for creating new programs and transformation programs by anyone who needs to do this.

Transforming a single record using a XSLT

This is the GXSLT_Rec2Rec class (included in the metadata broker library), which performs the actual conversion:

package org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.GXSLT_Rec2Rec;

import org.apache.log4j.Logger;
import org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.Program;
import org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.RecordType;
import org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.VariableType;

import java.io.StringReader;
import java.io.StringWriter;
import java.rmi.RemoteException;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

public class GXSLT_Rec2Rec implements Program {
	
	private static Logger log = Logger.getLogger(GXSLT_Rec2Rec.class);
	private StringWriter output;
	private static TransformerFactory factory = TransformerFactory.newInstance();
		
	public void transform(RecordType record, VariableType xslt, RecordType outRecord) throws RemoteException {
		try {
	            Transformer t = factory.newTransformer(new StreamSource(new StringReader(xslt.getReference())));
        	    t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
	            output = new StringWriter();
        	    t.transform(new StreamSource(new StringReader(record.getReference())), new StreamResult(output));
	        } catch(Exception e) {
        		log.error("Failed to transform record. Throwing exception.");
        		throw new RemoteException(e.toString());
	        }
	}
	
	public void transform(String record, Templates xslt) throws RemoteException {
	       try {
	            Transformer t = xslt.newTransformer();
	            t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
	            output = new StringWriter();
	            t.transform(new StreamSource(new StringReader(record)), new StreamResult(output));
	        } catch(Exception e) {
	        	log.error("Failed to transform record. Throwing exception.");
	        	throw new RemoteException(e.toString());
	        }
	}
	
	public static Templates compileXSLT(String xslt) throws TransformerConfigurationException {
		return factory.newTemplates(new StreamSource(new StringReader(xslt)));
	}
	
	public String getOutput() {
		return output.toString();
	}	
}

The only transformation method that can be used externally (when this program is called by a transformation program) is 'public void transform(RecordType record, VariableType xslt, RecordType outRecord)'. The other 'transform' method as well as the 'compileXSLT' method are intended to be used internally by other programs which call GXSLT_Rec2Rec during their execution.

This is the XML definition of the transformation program:

	 
<?xml version="1.0" encoding="UTF-8"?>	 
<TransformationProgram>
	<Input name="TPInput">
		<Schema isVariable="true" />
		<Language isVariable="true" />
		<Type>record</Type>
		<Reference isVariable="true" />
	</Input>
	<Variable name="XSLT" />
	<Output name="TPOutput">
		<Schema isVariable="true" />
		<Language isVariable="true" />
		<Type>record</Type>
	</Output>
	<TransformationRule>
		<Definition>
		<Transformer>org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.GXSLT_Rec2Rec.GXSLT_Rec2Rec</Transformer>
		<Input name="Rule1Input1">
			<Schema isVariable="true"> //Input[@name='TPInput']/Schema </Schema>
			<Language isVariable="true"> //Input[@name='TPInput']/Language </Language>
			<Type>record</Type>
			<Reference isVariable="true"> //Input[@name='TPInput']/Reference </Reference>
		</Input>
		<Input name="Rule1Input2">
			<Schema />
			<Language />
			<Type>variable</Type>
			<Reference isVariable="true"> //Variable[@name='XSLT'] </Reference>
		</Input>
		<Output name="TPRule1Output">
			<Reference>//Output[@name='TPOutput']</Reference>
		</Output>
		</Definition>
	</TransformationRule>
</TransformationProgram>

In this example, the transformation program defined above is stored in the DIS as a profile with UniqueID=ce6b9860-ebfe-11db-8b69-dd428ed9686d. The input record that is going to be transformed is stored in a local file named input.xml, and the XSLT that will be used is defined by the file xslt.xml. The following code fragment reads the contents of these two files, creates a set of parameters which are used in order to assign the input data and the XSLT definition to the respective transformation program variable inputs, and then invokes the transform operation of the metadata broker service. The result is written to the console. The URI of the remote service is given as a command-line argument.

public class Client {
	public static void main(String[] args) {
		try {
			// Get the broker service porttype
			EndpointReferenceType endpoint = new EndpointReferenceType();
			endpoint.setAddress(new Address(args[0]));
			MetadataBrokerPortType broker = new MetadataBrokerServiceAddressingLocator().getMetadataBrokerPortTypePort(endpoint);

			// Read the input data file into a string
			String inputData = readTextFile("input.xml");

			// Read the XSLT file into a string
			String XSLTDefinition = readTextFile("xslt.xml");
				
			// Create a set of transformation parameters, assigning values to variables
			// defined in the transformation program
			TransformationParameters tparams = TransformationParameters.newInstance();
			tparams.addParameter("//Input[@name='TPInput']/Schema", "Schema1=URI1");
			tparams.addParameter("//Input[@name='TPInput']/Language", "en");
			tparams.addParameter("//Input[@name='TPInput']/Reference", inputData);
			tparams.addParameter("//Output[@name='TPOutput']/Schema", "Schema2=URI2");
			tparams.addParameter("//Output[@name='TPOutput']/Language", "en");
			tparams.addParameter("//Variable[@name='XSLT']", XSLTDefinition);

			// Prepare the invocation parameters
			TransformWithNewTP params = new TransformWithNewTP();
			params.setTransformationProgramID("ce6b9860-ebfe-11db-8b69-dd428ed9686d");
			params.setParameters(tparams.getAsString());

			// Invoke the remote operation and write the result to the console
			System.out.println(broker.transform(params));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
		 
	private static String readTextFile(String filename) throws IOException {
		BufferedReader br = new BufferedReader(new FileReader(filename));
		StringBuffer buf = new StringBuffer();
		String tmp;
		while ((tmp = br.readLine()) != null) {
			buf.append(tmp + "\n");
		} 
		br.close();
		return buf.toString();
	}
}

Transforming an entire ResultSet using a XSLT

This is the definition of the GXSLT_RS2RS class (included in the metadata broker library), which performs the actual conversion:

package org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.GXSLT_RS2RS;

import java.rmi.RemoteException;

import org.apache.log4j.Logger;
import org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.Program;
import org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.ResultSetType;
import org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.VariableType;
import org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.GXSLT_RS2RS.GXSLT_RS2RS_Worker;
import org.diligentproject.searchservice.searchlibrary.rsclient.elements.RSResourceWSRFType;
import org.diligentproject.searchservice.searchlibrary.rswriter.RSXMLWriter;

public class GXSLT_RS2RS implements Program {
	private static Logger log = Logger.getLogger(GXSLT_RS2RS.class);
	private String output = null; 

	public void transform(ResultSetType RS, VariableType xslt, ResultSetType outRS) throws RemoteException {
		try {
			RSXMLWriter writer = RSXMLWriter.getRSXMLWriter();
			new GXSLT_RS2RS_Worker(RS, writer, xslt).start();
			output = writer.getRSLocator(new RSResourceWSRFType()).getLocator();
		} catch (Exception e) {
			log.error("GXSLT_RS2RS: Failed to create writer for output resultset.", e);
			throw new RemoteException("GXSLT_RS2RS: Failed to create writer for output resultset.", e);
		}
	}

	public String getOutput() {
		return this.output;
	}

}

As stated before, bulk transformations are non-blocking. For this reason, the above code spawns a new thread to handle the transformation process. The definition of the GXSLT_RS2RS_Worker class (which extends the Thread class) follows.

package org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.GXSLT_RS2RS;

import javax.xml.transform.Templates;
import org.apache.log4j.Logger;
import org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.ResultSetType;
import org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.VariableType;
import org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.GXSLT_Rec2Rec.GXSLT_Rec2Rec;
import org.diligentproject.searchservice.searchlibrary.resultset.elements.ResultElementGeneric;
import org.diligentproject.searchservice.searchlibrary.rsclient.elements.RSLocator;
import org.diligentproject.searchservice.searchlibrary.rsclient.elements.RSResourceLocalType;
import org.diligentproject.searchservice.searchlibrary.rsreader.RSXMLIterator;
import org.diligentproject.searchservice.searchlibrary.rsreader.RSXMLReader;
import org.diligentproject.searchservice.searchlibrary.rswriter.RSXMLWriter;

class GXSLT_RS2RS_Worker extends Thread {
	private static Logger log = Logger.getLogger(GXSLT_RS2RS_Worker.class);
	private ResultSetType RS;
	private RSXMLWriter writer;
	private VariableType xslt;

	public GXSLT_RS2RS_Worker(ResultSetType resultSet, RSXMLWriter RSWriter, VariableType xsltToUse) {
		RS = resultSet;
		writer = RSWriter;
		xslt = xsltToUse;
	}

	public void run() {
		String element = null;
		int i = 0;
	
		try {
			/* Compile the XSLT so that the records in the resultset will be transformed faster */
			Templates compiledXSLT = GXSLT_Rec2Rec.compileXSLT(xslt.getReference());

			/* Read each record of the input ResultSet, use the GXSLT_Rec2Rec program in order to transform 
			 * it and add it to the output ResultSet. */
			GXSLT_Rec2Rec GXSLTRecProgram = new GXSLT_Rec2Rec();
			RSXMLReader reader = RSXMLReader.getRSXMLReader(new RSLocator(RS.toString()));
			RSXMLIterator iter = reader.makeLocalPatiently(new RSResourceLocalType(), 1200000).getRSIterator(1200000);
			while(iter.hasNext()) {
				if (writer.isTimerAlive())
					writer.resetTimer();
				ResultElementGeneric elem = (ResultElementGeneric)iter.next(ResultElementGeneric.class);
				if (elem == null)
					continue;
				element = elem.getPayload();
				GXSLTRecProgram.transform(element, compiledXSLT);
				writer.addResults(new ResultElementGeneric(elem.getRecordAttributes(ResultElementGeneric.RECORD_ID_NAME)[0].getAttrValue(),
					elem.getRecordAttributes(ResultElementGeneric.RECORD_COLLECTION_NAME)[0].getAttrValue(),
					GXSLTRecProgram.getOutput()));
				i++;
				element = null;
			}
			writer.close();
		} catch (Exception e) {
			if (element != null)
				i++;
			log.error("GXSLT_RS2RS: Failed to transform the given resultset. Stopped at element " + String.valueOf(i) + ":\n" + element, e);
			e.printStackTrace();
			try {
				writer.close();
			} catch (Exception e1) {
				log.error("GXSLT_RS2RS: Failed to close resultset.");
			}
		}
	}
}

The above code uses the GXSLT_Rec2Rec program to compile the XSLT so that the transformation executes as fast as possible. Then it iterates over the whole set of elements contained in the ResultSet, transforming each one using the compiled XSLT. Each transformed element is then added to the output ResultSet.

The following is the XML definition of the transformation program used for this type of transformation.

<TransformationProgram>
	<Input name="TPInput">
		<Schema isVariable="true" />
		<Language isVariable="true" />
		<Type>resultset</Type>
		<Reference isVariable="true" />
	</Input>
	<Variable name="XSLT" />
	<Output name="TPOutput">
		<Schema isVariable="true" />
		<Language isVariable="true" />
		<Type>resultset</Type>
	</Output>
	<TransformationRule>
		<Definition>
			<Transformer>org.diligentproject.metadatamanagement.metadatabrokerlibrary.programs.GXSLT_RS2RS.GXSLT_RS2RS</Transformer>
			<Input name="Rule1Input1">
				<Schema isVariable="true"> //Input[@name='TPInput']/Schema </Schema>
				<Language isVariable="true"> //Input[@name='TPInput']/Language </Language>
				<Type>resultset</Type>
				<Reference isVariable="true"> //Input[@name='TPInput']/Reference </Reference>
			</Input>
			<Input name="Rule1Input2">
				<Schema />
				<Language />
				<Type>variable</Type>
				<Reference isVariable="true"> //Variable[@name='XSLT'] </Reference>
			</Input>
			<Output name="TPRule1Output">
				<Reference>//Output[@name='TPOutput']</Reference>
			</Output>
		</Definition>
	</TransformationRule>
</TransformationProgram>

In this example, the transformation program defined above is stored in the DIS as a profile with UniqueID=eb46fc40-ebfe-11db-8b6b-dd428ed9686d. The EPR of the input ResultSet that is going to be transformed is stored in a local file named input.xml, and the XSLT that will be used is defined by the file xslt.xml. The URI of the remote service is given as a command-line argument. The client code that invokes the broker service and performs the transformation is the same as in the previous example. The only thing that changes is the ID of the transformation program that is called, which should be set to eb46fc40-ebfe-11db-8b6b-dd428ed9686d.

Using a transformation program within another transformation program

As stated before, whole transformation programs can be used as 'black-box' components inside another transformation program. This can be done by defining a transformation rule which describes the call to the second transformation program.

The transformation program that will be called from another transformation program in this example is defined below.

<TransformationProgram>
    <Input name="TPInput">
        <Schema>SCH1=http://schema1.xsd</Schema>
        <Language>en</Language>
        <Type>resultset</Type>
        <Reference isVariable="true" />
    </Input>
    <Output name="TPOutput">
        <Schema>SCH3=http://schema3.xsd</Schema>
        <Language>en</Language>
        <Type>resultset</Type>
    </Output>
    <TransformationRule>
        <Definition>
            <Transformer>org.diligentproject.program2</Transformer>
            <Input name="Rule2Input">
                <Schema isVariable="true">//Output[@name='TPRule1Output']/Definition/Schema</Schema>
                <Language isVariable="true">//Output[@name='TPRule1Output']/Definition/Language</Language>
                <Type>resultset</Type>
                <Reference isVariable="true">//Output[@name='TPRule1Output']/Definition/Reference</Reference>
            </Input>
            <Output name="Rule2Output">
                <Reference>//Output[@name='TPOutput']</Reference>
            </Output>
        </Definition>
    </TransformationRule>
</TransformationProgram>

The input and output schemas and languages are predefined inside this transformation program, so the only thing that should be specified at run-time is the actual input data reference. Let's say that this transformation program is stored in the DIS and its UniqueID is 910e0710-f251-11db-88f9-f971eaf0d653.

The transformation program that uses the above transformation program is defined below.

<TransformationProgram>
    <Input name="TPInput">
        <Schema>SCH1=http://schema1.xsd</Schema>
        <Language>en</Language>
        <Type>resultset</Type>
        <Reference isVariable="true" />
    </Input>
    <Variable name="var1"/>
    <Output name="TPOutput">
        <Schema>SCH2=http://schema2.xsd</Schema>
        <Language>en</Language>
        <Type>resultset</Type>
    </Output>
    <TransformationRule>
        <Reference>
            <Program>910e0710-f251-11db-88f9-f971eaf0d653</Program>
            <Value isVariable="true" target="//Input[@name='TPInput']/Reference">//Input[@name='TPInput']/Reference</Value>
            <Output name="Rule1Output" />
        </Reference>
    </TransformationRule>
    <TransformationRule>
        <Definition>
            <Transformer>org.diligentproject.program1</Transformer>
            <Input name="Rule2Input1">
                <Schema isVariable="true">//Output[@name='TPRule1Output']/Definition/Schema</Schema>
                <Language isVariable="true">//Output[@name='TPRule1Output']/Definition/Language</Language>
                <Type>resultset</Type>
                <Reference isVariable="true">//Output[@name='TPRule1Output']/Definition/Reference</Reference>
            </Input>
            <Input name="Rule2Input2">
                <Schema />
                <Language />
                <Type>variable</Type>
                <Reference isVariable="true"> //Variable[@name='var1'] </Reference>
            </Input>
            <Output name="Rule2Output">
                <Reference>//Output[@name='TPOutput']</Reference>
            </Output>
        </Definition>
    </TransformationRule>
</TransformationProgram>

The element that describes the call to the first transformation program is the first TransformationRule element. This element specifies the UniqueID of the transformation program to be called, as well as a mapping of values to the variable inputs of that transformation program. Since the first transformation program contains only one variable input (the input data reference), there is only one mapping in this example, described by a Value element. The target attribute of this element specifies the target element of the other transformation program whose value is to be set, and the element's content specifies the actual value to set. In this example, this is not a literal value but a reference to another element of the transformation program, where the value should be taken from. Specifically, we have specified that the first transformation program's input should be the input of the second transformation program. Since the value of the Value element is a XPath expression, the isVariable attribute is also set to true, meaning that the content should be interpreted as a reference to another element and not as a literal value. The output of the first transformation program becomes the output of the transformation rule that called it, and is named Rule1Output. This output is then used as the input of the next transformation rule.

-- Sboutsis 15:40, 29 May 2007 (EEST)