Basic tutorials

Quick start

Download the full binary release, unzip reflex-0.4.0-full-bin.zip and run the examples from the reflex/ directory.

"Hello world", multiple publication with XSLT, DTD validation and XInclusion.

Hello world !

This example is somewhat useless. I did it only to keep alive a great tradition in computer sciences :p

Its purpose is to show that everything works fine, and to give an overview of the basics of Active Tags.

  1. Get Java (1.4.2, 1.5.x or 1.6.x)
  2. Download the full binary release of RefleX
  3. Unzip reflex-0.4.0-full-bin.zip
  4. Run the examples from the reflex/ directory.
     $ cd /path/to/reflex/

First active document

This template simply displays a tiny XML document with your name. It contains a mix of XML instructions among XML litterals; in Active Tags, templates are called active documents.

[doc/tutorial/basic/hello/hello-world-template.xcl]

<?xml version="1.0" encoding="iso-8859-1"?>
<!--create an XML document--> <example xmlns:sys="http://ns.inria.org/active-tags/sys" xmlns:xcl="http://ns.inria.org/active-tags/xcl"> <!--get the system property "who"--> <xcl:set name="who" value="{ string( $sys:env/who ) }"/> <!--set a default value if the system property was missing--> <xcl:set xcl:if="{ not( $who ) }" name="who" value="world"/> <!--the XML content as litterals--> <!--{ $who } will be substituted by its value--> <title>Hello { $who } !</title> <para>This example show how to create dynamically an XML document.</para> <a href="http://reflex.gforge.inria.fr">Powered by RefleX.</a> </example>

2 modules are involved : the XCL module and the SYSTEM module (they are referred by their respective namespace; the two xmlns declarations act like a "library import" in other languages); the former perform operations (<xcl:set> to assign a value to a name, @xcl:if to perform a test), the latter is used to retrieve a predefined property ($sys:env contains the environment variables that has to be set externally); the other XML tags are not bound to a module, they are just litterals used to produce the expected XML structure. Of course, XML litterals can also have namespaces if necessary.

XPath expressions always stand in curly braces. They can be found in text content, in attributes of XML litteral elements, or in attributes of XML active tags.

Open a console and at the prompt, type the following command from the RefleX home directory (note that the (line cut) (line cut) icon means that you MUST NOT insert a line break; ensure that "(lien cut)" doesn't appear in your own console) :

 $ java -Dwho="John Doe" -jar reflex-0.4.0.jar (line cut)
     run doc/tutorial/basic/hello/hello-world-template.xcl

This command consist on running the RefleX command line interface (CLI) with Java; the last parameters indicates to the CLI to run the actual active sheet; -Dwho="John Doe" consist on setting a Java environment variable that the active sheet will use.

By default, the result document is automatically serialized by RefexCLI (the entry class in the jar file). The output is a tiny XML document displayed on the standard output :

<?xml version="1.0" encoding="UTF-8"?>
<example>
<title>Hello John Doe !</title>
<para>This example show how to create dynamically an XML document.</para>
<a href="http://reflex.gforge.inria.fr">Powered by RefleX.</a>
</example>

As expected, all XPath instructions and active tags have been replaced by their result.

First active sheet

If the root element is not a litteral (like <example> above) but an active tag, the script is no longer called an active document, but rather an active sheet. The following active sheet perform the same as before, but the template is embedded in the <xcl:document> element that creates a variable which is referred in the <xcl:transform> element.

[doc/tutorial/basic/hello/hello-world.xcl]

<?xml version="1.0" encoding="iso-8859-1"?>
<xcl:active-sheet xmlns:sys="http://ns.inria.org/active-tags/sys" xmlns:xcl="http://ns.inria.org/active-tags/xcl"> <!--get the system property "who"--> <xcl:set name="who" value="{ string( $sys:env/who ) }"/> <!--set a default value if the system property was missing--> <xcl:set xcl:if="{ not( $who ) }" name="who" value="world"/> <!--create an XML document--> <xcl:document name="xmlDoc"> <!--the XML content as litterals--> <example> <!--{ $who } will be substituted by its value--> <title>Hello { $who } !</title> <para>This example show how to create dynamically an XML document.</para> <a href="http://reflex.gforge.inria.fr">Powered by RefleX.</a> </example> </xcl:document> <!--serialize the XML document to the standard system output ; as the transformation doesn't involve a stylesheet, a copy is performed--> <xcl:transform output="{ $sys:out }" source="{ $xmlDoc }"/> </xcl:active-sheet>

Other active stuffs are introduced in this script : <xcl:active-sheet> (which is just a convenient root) and $sys:out (which hold the standard system output).

Type in a console :

 $ java -Dwho="John Doe" -jar reflex-0.4.0.jar (line cut)
     run doc/tutorial/basic/hello/hello-world.xcl

The output is the same XML document as previously, but on a single line because the default serializer used in the previous example was managing the indentation of the output (this default behaviour can be altered with an option of the CLI) whereas the serializer used here doesn't (this behaviour can also be altered by setting a parameter to the XSLT engine).

In order to get the same output, we replace the last active tag with:

    <xcl:transform output="{ $sys:out }" source="{ $xmlDoc }">
        <!-- set a parameter to the transformer -->
        <xcl:param name="xsl:indent" value="yes"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"/>
    </xcl:transform>

First HTML transformation with XSLT

As explained in the comments of the active sheet above, the transformation doesn't involve a stylesheet, which means that the XML datas are copied as-is : the transformation doesn't act on the structure, but on the nature of the document : the XML logic structure is transformed to an XML stream of characters written in the standard output. So, let's use a stylesheet !

Our stylesheet (doc/tutorial/basic/hello/example.xsl) simply transforms the XML structure to HTML. Additionally, we are serializing the stream result in a file instead of the standard output. A single line has been changed in the source code :

  <xcl:transform output="example.html" source="{ $xmlDoc }" stylesheet="example.xsl"/>

Run the example :

 $ java -Dwho="John Doe" -jar reflex-0.4.0.jar (line cut)
     run doc/tutorial/basic/hello/hello-world-html.xcl

...and open doc/tutorial/basic/hello/example.html in your favorite browser.

First Web application

We go on with the same example but in a Web environment. The following HTTP service simply produces dynamically the same XML document, but this time within a Web server when it receives an HTTP request from a Web client. The service accept a parameter in the HTTP request (the name of the guy to say "hello").

[doc/tutorial/basic/hello/web/WEB-INF/hello-world.web]

<?xml version="1.0" encoding="iso-8859-1"?>
<web:service xmlns:web="http://ns.inria.org/active-tags/web" xmlns:xcl="http://ns.inria.org/active-tags/xcl"> <!-- [webapp]/index.xml --> <web:mapping match="^/index\.xml$"> <!--get the parameter from the query string : ...index.xml?who=John+Doe --> <xcl:set name="who" value="{ string( $web:request/who ) }"/> <!--set a default value if the parameter was missing--> <xcl:set xcl:if="{ not( $who ) }" name="who" value="world"/> <!--create an XML document--> <xcl:document name="xmlDoc"> <!--the XML content as litterals--> <example> <!--{ $who } will be substituted by its value--> <title>Hello { $who } !</title> <para>This example show how to create dynamically an XML document.</para> <a href="http://reflex.gforge.inria.fr">Powered by RefleX.</a> </example> </xcl:document> <!--serialize the XML document to the HTTP output stream ; as the transformation doesn't involve a stylesheet, a copy is performed--> <xcl:transform source="{ $xmlDoc }" output="{ value( $web:response/@web:output ) }"/> </web:mapping> </web:service>

Another module is involved : it is the Web module, which allows to define HTTP services (<web:service>) ; a service is able to handle HTTP requests and select the <web:mapping> that matches the request URI upon a regular expression. The request URI tested starts from the Web application up to the query string (excluded).

The $web:request and $web:response predefined properties are referring to X-operable objects, that is to say to objects that behaves like XML nodes (they may have attributes and content accessible with XPath). As explained in the "troubleshooting section", the value() function is used to unwrap the value of an attribute, which is not a simple string in this example, but an object to which one can write to (actually, the output stream sent back to the client).

We use the RefleX servlet for this Web application, which uses this deployment descriptor. A default HTML welcome page invite the user to type its name in a form. The response sent back to the client is the same XML document as in the first examples (which will be displayed in a tree by most modern browsers).

Running the Web application :

To stop the server, hit CTRL-C in the console. If you make some changes in the active sheet, stop and start again the server.

Publication with XSLT

This example shows how to publish HTML files from a set of XML files. There is a batch publishing process which produces a set of static HTML files, and a Web application that transform the XML files dynamically.

A batch publication chain process

This script select all the XML files from a repository (the same directory which is used to build the Web application hereafter) ; the files selected are those in the subtree which ends by '.xml' and are not in the 'WEB-INF' directory. What is noticeable is that the set of XML files to transform is selected with an XPath expression. Then, they are transformed to HTML in the 'build' directory where the directory structure is the same as the directory structure of the repository. Two XSLT files are used : one to transform each purchase order, the other to transform a list of purchase orders.

(the directory structure used here is the same as those used in the counterpart Web exemple, that's why the files are located in the "web/" directory, and why there is a "WEB-INF/" subdirectory)

[doc/tutorial/basic/xslt/active-sheet.xcl]

<?xml version="1.0" encoding="iso-8859-1"?>
<xcl:active-sheet xmlns:io="http://ns.inria.org/active-tags/io" xmlns:xcl="http://ns.inria.org/active-tags/xcl"> <!-- where are the XML files : "web/" is the name of the directory relative to the directory where is located this active sheet : "file:///path/to/reflex/doc/tutorial/basic/xslt/web/" --> <xcl:set name="repository" value="{ io:file( 'web/' ) }"/> <!--parse 2 stylesheets--> <xcl:parse-stylesheet name="xslt-orders" source="web/WEB-INF/xslt/orders.xsl"/> <xcl:parse-stylesheet name="xslt-po" source="web/WEB-INF/xslt/po.xsl"/> <!-- select all XML files which are not in the 'WEB-INF/' directory --> <xcl:for-each name="file"
select="{ $repository//*[@io:is-file][@io:extension='xml'][name(..)!='WEB-INF'] }"> <!-- relocate files : from file:///path/to/reflex/doc/tutorial/basic/xslt/web/purchase-orders/1234.xml to file:///path/to/reflex/doc/tutorial/basic/xslt/build/purchase-orders/1234.html ("build/" is resolved upon the directory where is located this active sheet) --> <xcl:set name="target"
value="{ io:file( io:resolve-uri( $repository, $file, 'build/' ) ) }"/> <xcl:set name="target" value="{ substring-before( $target/@io:path , '.xml' ) }.html"/> <xcl:echo value="Transforming { io:relativize-uri( io:file('.') , $file ) }"/> <xcl:echo value=" to { io:relativize-uri( io:file('.') , io:file( $target ) ) }"/> <!--now, let's select the right stylesheet--> <xcl:if test="{ $file/ancestor::purchase-orders }"> <xcl:then> <!-- the file is under the "purchase-orders/" directory --> <xcl:set name="xslt" value="{ $xslt-po }"/> </xcl:then> <xcl:else> <xcl:set name="xslt" value="{ $xslt-orders }"/> </xcl:else> </xcl:if> <!--perform the transformation--> <xcl:transform output="{ $target }" source="{ $file }" stylesheet="{ $xslt }"/> </xcl:for-each> </xcl:active-sheet>

In the active sheet above, the I/O module is involved : the io:file() function creates an #io:x-file object which is XML friendly : an XPath expression can be applied on it, since it exposes some of its properties as XML attributes and for directories the files it contains as XML children.

The XML source files to transform are the list of purchase orders (also consider that it could be simply generated from the purchase orders files)...

[doc/tutorial/basic/xslt/web/index.xml]

<?xml version="1.0" encoding="UTF-8"?>
<orders> <order id="1234"/> <order id="5678"/> </orders>

...and two purchase orders :

[doc/tutorial/basic/xslt/web/purchase-orders/1234.xml]

<?xml version="1.0" encoding="UTF-8"?>
<order id="1234"> <dollar> <item part-number="321" price="138.95" quantity="1"> Lawnmower </item> <item part-number="654" price="29.99" quantity="2"> Baby monitor </item> </dollar> <euro> <item part-number="987" price="11.27" quantity="3"> Roquefort Cheese </item> </euro> </order>

[doc/tutorial/basic/xslt/web/purchase-orders/5678.xml]

<?xml version="1.0" encoding="UTF-8"?>
<order id="5678"> <euro> <item part-number="123" price="0.00" quantity="4"> RefleX full distribution </item> </euro> </order>

Generate the HTML pages :

 $ java -jar reflex-0.4.0.jar (line cut)
     run doc/tutorial/basic/xslt/active-sheet.xcl

...and open doc/tutorial/basic/xslt/build/index.html in your favorite browser.

View of the HTML publication (stored on the file system) :

[www]
Purchase orders - Web Navigator

A dynamic Web application

This service simply performs the same transformations as in the batch publishing process, but dynamically : the result HTML pages are not stored but delivered on the fly to the Web client.

[doc/tutorial/basic/xslt/web/WEB-INF/active-sheet.web]

<?xml version="1.0" encoding="iso-8859-1"?>
<web:service xmlns:web="http://ns.inria.org/active-tags/web" xmlns:xcl="http://ns.inria.org/active-tags/xcl"> <web:init> <!--the 2 stylesheets will be shared by all HTTP requests--> <xcl:parse-stylesheet name="xslt-orders" source="web:///WEB-INF/xslt/orders.xsl" scope="shared"/> <xcl:parse-stylesheet name="xslt-po" source="web:///WEB-INF/xslt/po.xsl" scope="shared"/> </web:init> <!-- [webapp]/purchase-orders/*.html --> <web:mapping match="^/purchase-orders/(.*)\.html$"> <!--retrieving the expected purchase order in XML--> <xcl:parse name="xml"
source="web:///purchase-orders/{ string( $web:request/@web:match ) }.xml"/> <xcl:transform source="{ $xml }" output="{ value( $web:response/@web:output ) }"
stylesheet="{ $xslt-po }"/> </web:mapping> <!-- [webapp]/*.html --> <web:mapping match="^/(.*)\.html$"> <!--get the list of orders in XML--> <xcl:parse name="xml" source="web:///{ string( $web:request/@web:match ) }.xml"/> <xcl:transform source="{ $xml }" output="{ value( $web:response/@web:output ) }"
stylesheet="{ $xslt-orders }"/> </web:mapping> </web:service>

The <web:init> element is invoked by the service when the server starts. Notice the @scope attribute of the <xcl:parse-stylesheet> element that makes the properties created shared by all HTTP requests. Most of the active tags designed to create a property are using this attribute to specify its scope.

The web:/// scheme allows to get a resource located within the Web application, wherever it is deployed on the file system.

In the regular expressions used in the mappings, the construct "(.*)" allows to capture groups of strings that are returned by the @web:match attribute of the $web:request property.

Running the Web application :

  • Start the server from a console :
     $ java -cp reflex-0.4.0.jar:xunit-0.4.0.jar winstone.Launcher (line cut)
        --httpPort=9999 --webroot=doc/tutorial/basic/xslt/web/
  • Open a Web browser to http://localhost:9999/

To stop the server, hit CTRL-C in the console.

DTD validation and XInclusion

See also

An XML validation can be performed while parsing with a DTD or a W3C XML Schema if your parser support it; you just have to turn on the relevant option (please refer to the documentation of your parser). A validation can be performed by the parser while parsing ; this can be specified simply with the @validate attribute of the <xcl:parse> element. Other attributes are usefull : @mode to allow to parse XML fragments, and @style to choose the parsing style (DOM or SAX).

  <xcl:parse mode="strict" name="xmlDoc" source="file:///path/to/input.xml" style="stream"
validate="yes"/>

Notice that with the Active Schema Language you will be able to design smartest schemas and perform smartest validations; you should have a look at the schema tutorial.

See also

Other pipelines & filters tutorials are available.

[doc/tests/xunit/xop/5.1/apply-sequence.xcl]

<?xml version="1.0" encoding="iso-8859-1"?>
<xcl:active-sheet xmlns:xcl="http://ns.inria.org/active-tags/xcl" xmlns:sys="http://ns.inria.org/active-tags/sys"> <!-- environment variables : -input : the input file to process -output : the output file -stylesheet : the XSLT file --> <!--get an XInclude filter--> <xcl:parse-filter name="xinclude" source="http://www.w3.org/2001/XInclude"/> <!--get the XSLT--> <xcl:parse-stylesheet name="xslt" source="{ string( $sys:env/stylesheet ) }"/> <!--connect a pipeline--> <xcl:parse name="input" source="{ string( $sys:env/input ) }" style="stream" mode="strict"
validate="yes"/> <xcl:filter name="included" source="{ $input }" filter="{ $xinclude }"/> <xcl:transform source="{ $included }" output="{ string( $sys:env/output ) }"
stylesheet="{ $xslt }"/> <!--if an error occurs, we are producing a report, as an XML document--> <xcl:fallback> <xcl:if test="{ $xml:error }"> <xcl:then> <xcl:document name="error"> <xcl:element name="{ name( $xml:error ) }">{ $xml:error/@column }{ $xml:error/@line }{ $xml:error/@cause } <message>{ string( $xml:error/@message )}</message> { $xml:error} { }</xcl:element> </xcl:document> <xcl:transform source="{ $error }" output="{ $sys:err }"/> </xcl:then> <xcl:else> <xcl:echo value="{ $xcl:error }"/> </xcl:else> </xcl:if> <xcl:exit/> </xcl:fallback> </xcl:active-sheet>

The pipeline simply parses and validates an input, then passes it through an XInclude filter, then transforms the result with a stylesheet to an output file.

If the input is malformed or invalid, an XML document that describes the error is built and displayed. Like in the first tutorial, the XML input file, the XSLT stylesheet, and the output result file are set to the JVM environment.

  • Test with a valid XML input :

    [doc/tests/xunit/xop/5.1/valid.xml]

    <?xml version="1.0" encoding="iso-8859-1"?>
    <root> <a>Test</a> <b><a/></b> <xi:include href="some-content-to-include.xml" parse="xml" xmlns:xi="http://www.w3.org/2001/XInclude"/> </root>
     $ java -Dinput=valid.xml -Doutput=valid-out.xml (line cut)
         -Dstylesheet=stylesheet.xsl -jar reflex-0.4.0.jar (line cut)
         run doc/tests/xunit/xop/5.1/apply-sequence.xcl
    The result is simply the transformed output :

    [doc/tests/xunit/xop/5.1/valid-out.xml]

    <?xml version="1.0" encoding="iso-8859-1"?>
    <!-- <root> -> <ROOT> <a> -> <A> <b> -> <B> <c> -> <C> --> <ROOT> <A>Test</A> <B><A/></B> <C>Some content to include</C> </ROOT>
  • Test with a malformed XML input, which is just a variation of the previous one :
     $ java -Dinput=malformed.xml -Doutput=malformed-out.xml (line cut)
         -Dstylesheet=stylesheet.xsl -jar reflex-0.4.0.jar (line cut)
         run doc/tests/xunit/xop/5.1/apply-sequence.xcl
    The output is not produced, but a fatal error is reported to the standard error output ($sys:err) :

    [doc/tests/xunit/xop/5.1/malformed-error.xml]

    <?xml version="1.0" encoding="iso-8859-1"?>
    <xml:fatal-error column="16" line="11"> <message>The element type "a" must be terminated by the matching end-tag "</a>".</message> org.xml.sax.SAXParseException </xml:fatal-error>
  • Test with an invalid XML input, which is also a variation of the valid input :
     $ java -Dinput=invalid.xml -Doutput=invalid-out.xml (line cut)
         -Dstylesheet=stylesheet.xsl -jar reflex-0.4.0.jar (line cut)
         run doc/tests/xunit/xop/5.1/apply-sequence.xcl
    The output is not produced, but an error is reported to the standard error output :

    [doc/tests/xunit/xop/5.1/invalid-error.xml]

    <?xml version="1.0" encoding="iso-8859-1"?>
    <xml:error column="12" line="11"> <message>The content of element type "b" is incomplete, it must match "(a)+".</message> org.xml.sax.SAXParseException </xml:error>

Of course, you can test yourself what happens when for example you submit an input file that doesn't exist, or when you change the parsing style from "stream" to "tree"...