blob: 113138b46758f770583c85fe3ea5dec85615cd5b [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
<!--
~ Licensed to the Apache Software Foundation (ASF) under one
~ or more contributor license agreements. See the NOTICE file
~ distributed with this work for additional information
~ regarding copyright ownership. The ASF licenses this file
~ to you under the Apache License, Version 2.0 (the
~ "License"); you may not use this file except in compliance
~ with the License. You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing,
~ software distributed under the License is distributed on an
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
~ KIND, either express or implied. See the License for the
~ specific language governing permissions and limitations
~ under the License.
-->
<book>
<bookinfo>
<title>Axiom Developer Guide</title>
<releaseinfo>&version;</releaseinfo>
<legalnotice>
<para>
Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See
the NOTICE file distributed with this work for additional information regarding copyright ownership. The
ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the License at
</para>
<para>
<ulink url="http://www.apache.org/licenses/LICENSE-2.0"/>
</para>
<para>
Unless required by applicable law or agreed to in writing, software distributed under the License is
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing permissions and limitations under the
License.
</para>
</legalnotice>
</bookinfo>
<toc/>
<chapter>
<title>Testing</title>
<section>
<title>Unit test organization</title>
<para>
Historically, all unit tests were placed in the <filename>axiom-tests</filename> project.
One specific problem with this is that since all tests are in a common Maven module
which depends on both <filename>axiom-impl</filename> and <filename>axiom-dom</filename>,
it is not rare to see DOOM tests that accidentally use the LLOM implementation (which is the default).
The project description in <filename>axiom-tests/pom.xml</filename> indicates that it
was the intention to split the <filename>axiom-tests</filename> project into several parts
and make them part of <filename>axiom-api</filename>, <filename>axiom-impl</filename> and
<filename>axiom-dom</filename>. This reorganization is not complete
yet<footnote><para>See <ulink url="https://issues.apache.org/jira/browse/WSCOMMONS-419">WSCOMMONS-419</ulink>.</para></footnote>.
For new test cases (or when refactoring existing tests), the following guidelines should be applied:
</para>
<orderedlist>
<listitem>
<para>
Tests that validate the code in <filename>axiom-api</filename> and that do not require
an Axiom implementation to execute should be placed in <filename>axiom-api</filename>.
This primarily applies to tests that validate utility classes in <filename>axiom-api</filename>.
</para>
</listitem>
<listitem id="test.category.api">
<para>
The code of unit tests that apply to all Axiom implementations and that check conformance
to the specifications of the Axiom API should be added
to <filename>axiom-api</filename> and executed in <filename>axiom-impl</filename> and
<filename>axiom-dom</filename>. Currently, the recommended way is to create a
base class in <filename>axiom-api</filename> (with suffix <classname>TestBase</classname>) and
to create subclasses in <filename>axiom-impl</filename> and <filename>axiom-dom</filename>.
This makes sure that the DOOM tests never accidentally use LLOM (because
<filename>axiom-impl</filename> is not a dependency of <filename>axiom-dom</filename>).
</para>
</listitem>
<listitem>
<para>
Tests that check integration with other libraries should be placed in
<filename>axiom-integration</filename>. Note that this is the only module that requires
Java 1.5 (so that e.g. integration with JAXB2 can be tested).
</para>
</listitem>
<listitem>
<para>
Tests related to code in <filename>axiom-api</filename> and requiring an Axiom
implementation to execute, but that don't fall into category <xref linkend="test.category.api"/>
should stay in <filename>axiom-tests</filename>.
</para>
</listitem>
</orderedlist>
</section>
<section>
<title>Testing Axiom with different StAX implementations</title>
<para>
The following StAX implementations are available to test compatibility with Axiom:
</para>
<variablelist>
<varlistentry>
<term>Woodstox</term>
<listitem>
<para>
This is the StAX implementation that Axiom uses by default.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Sun Java Streaming XML Parser (SJSXP)</term>
<listitem>
<para>
This implementation is available as Maven artifact <literal>com.sun.xml.stream:sjsxp:1.0.1</literal>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>StAX Reference Implementation</term>
<listitem>
<para>
The reference implementation was written by BEA and is available as Maven artifact
<literal>stax:stax:1.2.0</literal>. The homepage is <ulink url="http://stax.codehaus.org/Home"/>.
Note that the JAR doesn't contain the necessary files to enable service discovery.
Geronimo's implementation of the StAX API library will not be able to locate
the reference implementation unless the following system properties are set:
</para>
<programlisting>javax.xml.stream.XMLInputFactory=com.bea.xml.stream.MXParserFactory
javax.xml.stream.XMLOutputFactory=com.bea.xml.stream.XMLOutputFactoryBase</programlisting>
</listitem>
</varlistentry>
<varlistentry>
<term>XL XP-J</term>
<listitem>
<para>
<quote>XL XML Processor for Java</quote> is IBM's implementation of StAX 1.0
and is part of IBM's JRE/JDK v6. Note that due
to an agreement between IBM and Sun, IBM's Java implementation for the Windows
platform is not available as a separate download, but only bundled with another
IBM product, e.g. <ulink url="http://www.ibm.com/developerworks/downloads/ws/wasdevelopers/">WebSphere
Application Server for Developers</ulink>.
</para>
<para>
On the other hand, the JDK for Linux can be downloaded as a separate package from the
<link url="https://www.ibm.com/developerworks/java/jdk/linux/download.html">developerWorks
site</link>. There are versions for 32-bit x86 (<quote>xSeries</quote>) and 64-bit AMD.
They are available as RPMs and tarballs. To install the JDK properly on a Debian
based system (including Ubuntu), follow the instructions given in
<xref linkend="install.ibm.jdk"/>.
</para>
</listitem>
</varlistentry>
</variablelist>
</section>
</chapter>
<chapter>
<title>OSGi integration</title>
<section>
<title>Requirements</title>
<formalpara id="osgi-req-no-separate-bundles">
<title>Requirement 1</title>
<para>
The Axiom artifacts SHOULD be usable both as normal JAR files and as OSGi bundles.
</para>
</formalpara>
<note>
<para>
The alternative would be to produce two sets of artifacts during the build. This
should be avoided in order to keep the build process as simple as possible.
It should also be noted that the Geronimo Spec artifacts also meet this requirement.
</para>
</note>
<formalpara id="osgi-req-same-api">
<title>Requirement 2</title>
<para>
All APIs defined by the <literal>axiom-api</literal> module, and in particular the
<classname>OMAbstractFactory</classname> API MUST continue to work
as expected in an OSGi environment, so that code in downstream projects
doesn't need to be rewritten.
</para>
</formalpara>
<formalpara id="osgi-req-same-impl-selection">
<title>Requirement 3</title>
<para>
<classname>OMAbstractFactory</classname> MUST select the same implementation
regardless of the type of container (OSGi or non OSGi). The only exception is
related to the usage of system properties to specify the default <classname>OMMetaFactory</classname>
implementation: in an OSGi environment, selecting an implementation class using
a system property is not meaningful.
</para>
</formalpara>
<note>
<para>
This is currently not the case. In a non OSGi environment, recent versions of Axiom
use JDK 1.3 service discovery to locate the default implementation and fall back to
LLOM if none is found. DOOM will never be selected as the default implementation.
On the other hand, the current OSGi integration will select any Axiom implementation
as default implementation, but gives priority to LLOM.
</para>
</note>
<formalpara id="osgi-ref-impl-not-exported">
<title>Requirement 4</title>
<para>
The bundles for the LLOM and DOOM implementations MUST NOT export any packages.
This is required to keep a clean separation between the public API and implementation
specific classes and to make sure that the implementations can be modified without the
risk of breaking existing code.
</para>
<para>
An exception MAY be made for factory classes related to foreign APIs, such as the
<classname>DocumentBuilderFactory</classname> implementation for an Axiom implementation
supporting DOM.
</para>
</formalpara>
<note>
<para>
When the Axiom artifacts are used as normal JAR files in a Maven build, this requirement implies that
they should be used in scope <literal>runtime</literal>.
</para>
<para>
Although this requirement is easy to implement for the Axiom project, there are
currently a couple of issues in the downstreams project that need to be addressed
to make this work:
</para>
<itemizedlist>
<listitem>
<para>
As explained in <ulink url="https://issues.apache.org/jira/browse/AXIS2-4902">AXIS2-4902</ulink>,
there are many places in Axis2 that still refer directly to Axiom implementation classes.
</para>
</listitem>
<listitem>
<para>
The <literal>axis2-saaj</literal> module is tightly coupled to <literal>axiom-dom</literal>.
Making this work will probably require using <literal>maven-shade-plugin</literal> to
include (a relocated copy of) the DOOM classes into <literal>axis2-saaj</literal>.
</para>
</listitem>
<listitem>
<para>
Abdera extends the LLOM implementation. Probably, some <literal>maven-shade-plugin</literal>
magic will be required here as well to create Abdera OSGi bundles that work properly with
the Axiom bundles.
</para>
</listitem>
</itemizedlist>
</note>
<formalpara id="osgi-req-dropin">
<title>Requirement 5</title>
<para>
It MUST be possible to use a non standard (third party) Axiom implementation as a drop-in replacement
for the standard LLOM and DOOM implementation, i.e. the <literal>axiom-impl</literal>
and <literal>axiom-dom</literal> bundles. It MUST be possible to replace <literal>axiom-impl</literal>
(resp. <literal>axiom-dom</literal>) by any Axiom implementation that supports the full Axiom API
(resp. that supports DOM in addition to the Axiom API), without the need to change any application code.
</para>
</formalpara>
<note>
<para>
This requirement has several important implications:
</para>
<itemizedlist>
<listitem>
<para>
It restricts the allowable exceptions to <xref linkend="osgi-ref-impl-not-exported"/>.
</para>
</listitem>
<listitem>
<para>
It implies that there must be an API that allows application code to select an Axiom
implementation based on its capabilities (e.g. DOM support) without introducing a
hard dependency on a particular Axiom implementation.
</para>
</listitem>
<listitem>
<para>
In accordance with <xref linkend="osgi-req-same-api"/> and <xref linkend="osgi-req-same-impl-selection"/>
this requirement not only applies to an OSGi environment, but extends to non OSGi environments as well.
</para>
</listitem>
</itemizedlist>
</note>
<formalpara>
<title>Requirement 6</title>
<para>
The OSGi integration SHOULD remove the necessity for downstreams projects
to produce their own custom OSGi bundles for Axiom. There SHOULD be one
and only one set of OSGi bundles for Axiom, namely the ones released by the Axiom project.
</para>
</formalpara>
<note>
<para>
Currently there are at least two projects that create their own modified Axiom bundles:
</para>
<itemizedlist>
<listitem>
<para>
Apache Geronimo has a custom Axiom bundle to support the Axis2 integration.
</para>
</listitem>
<listitem>
<para>
ServiceMix also has custom bundles to support Abdera; see
<ulink url="https://issues.apache.org/jira/browse/SMX4-877">SMX4-877</ulink>.
</para>
</listitem>
</itemizedlist>
<para>
Note that this requirement can't be satisfied directly by Axiom. It requires that
the above mentioned projects (Geronimo, Axis2 and Abdera) use Axiom in a way that is
compatible with its design, and in particular with <xref linkend="osgi-ref-impl-not-exported"/>.
Nevertheless, Axiom must provide the necessary APIs and features to meet the needs
of these projects.
</para>
</note>
<formalpara id="osgi-reg-no-framework">
<title>Requirement 7</title>
<para>
The Axiom OSGi integration SHOULD NOT rely on any particular OSGi framework such
as Felix SCR (Declarative Services). When deployed in an OSGi environment, Axiom should have the same
runtime dependencies as in a non OSGi environment (i.e. StAX, Activation and JavaMail).
</para>
</formalpara>
<note>
<para>
Axiom 1.2.12 relies on Felix SCR. Although there is no real issue with that, getting rid
of this extra dependency is seen as a nice to have. One of the reasons for using Felix SCR
was to avoid introducing OSGi specific code into Axiom. However, there is no issue with
having such code, provided that <xref linkend="osgi-req-no-osgi-dep"/> is satisfied.
</para>
</note>
<formalpara id="osgi-req-no-osgi-dep">
<title>Requirement 8</title>
<para>
In a non OSGi environment, Axiom MUST NOT have any OSGi related dependencies. That means
that the OSGi integration must be written in such a way that no OSGi specific classes are
ever loaded in a non OSGi environment.
</para>
</formalpara>
<formalpara id="osgi-req-best-practices">
<title>Requirement 9</title>
<para>
The OSGi integration MUST follow established best practices. It SHOULD be inspired by
what has been done to add OSGi integration to APIs that have a similar structure as Axiom.
</para>
</formalpara>
<note>
<para>
Axiom is designed around an abstract API and allows for the existence of multiple
independent implementations. A factory (<classname>OMAbstractFactory</classname>) is used to
locate and instantiate the desired implementation. This is similar to APIs such as
JAXP (<classname>DocumentBuilderFactory</classname>, etc.) and JAXB (<classname>JAXBContext</classname>).
These APIs have been successfully "OSGi-fied" e.g. by the Apache Geronimo project.
Instead of reinventing the wheel, we should leverage that work and adapt it to
Axiom's specific requirements.
</para>
<para>
It should be noted that because of the way the Axiom API is designed and taking into account
<xref linkend="osgi-req-same-api"/>, it is not possible to make Axiom entirely compatible
with OSGi paradigms (the same is true for JAXB). In an OSGi-only world, each Axiom
implementation would simply expose itself as an OSGi service (of type <classname>OMMetaFactory</classname> e.g.)
and code depending on Axiom would bind to one (or more) of these services depending on its needs.
That is not possible because it would conflict with <xref linkend="osgi-req-same-api"/>.
</para>
</note>
<formalpara>
<title>Non-Requirement 1</title>
<para>
APIs such as JAXP and JAXB have been designed from the start for inclusion into the JRE.
They need to support scenarios where an application bundles its own implementation
(e.g. an application may package a version of Apache Xerces, which would then be
instantiated by the <methodname>newInstance</methodname> method in
<classname>DocumentBuilderFactory</classname>). That implies that the selected implementation
depends on the thread context class loader. It is assumed that there is no such requirement
for Axiom, which means that in a non OSGi environment, the Axiom implementations are always loaded from the same
class loader as the <literal>axiom-api</literal> JAR.
</para>
</formalpara>
<note>
<para>
This (non-)requirement is actually not directly relevant for the OSGi support, but it
nevertheless has some importance because of <xref linkend="osgi-req-same-impl-selection"/>
(which implies that the OSGi support needs to be designed in parallel with the implementation
discovery strategy applicable in a non OSGi environment).
</para>
</note>
</section>
<section>
<title>Analysis of the Geronimo JAXB bundles</title>
<para>
As noted in <xref linkend="osgi-req-best-practices"/> the Apache Geronimo has successfully
added OSGi support to the JAXB API which has a structure similar to the Axiom API. This section briefly describes
how this works. The analysis refers to the following Geronimo artifacts:
<literal>org.apache.geronimo.specs:geronimo-jaxb_2.2_spec:1.0.1</literal> (called the "API bundle" hereafter),
<literal>org.apache.geronimo.bundles:jaxb-impl:2.2.3-1_1</literal> (the "implementation bundle"),
<literal>org.apache.geronimo.specs:geronimo-osgi-locator:1.0</literal> (the "locator bundle") and
<literal>org.apache.geronimo.specs:geronimo-osgi-registry:1.0</literal> (the "registry bundle"):
</para>
<itemizedlist>
<listitem>
<para>
The implementation bundle retains the <filename>META-INF/services/javax.xml.bind.JAXBContext</filename>
resource from the original artifact (<literal>com.sun.xml.bind:jaxb-impl</literal>).
In a non OSGi environment, that resource will be used to discover the implementation, following
the standard JDK 1.3 service discovery algorithm will (as required by the JAXB specification).
This is the equivalent of our <xref linkend="osgi-req-no-separate-bundles"/>.
</para>
</listitem>
<listitem>
<para>
The manifest of the implementation bundle has an attribute <literal>SPI-Provider: true</literal> that indicates
that it contains provider implementations that are discovered using the JDK 1.3 service discovery.
</para>
</listitem>
<listitem>
<para>
The registry bundle creates a <classname>BundleTracker</classname> that looks for
the <literal>SPI-Provider</literal> attribute in active bundles. For each bundle
that has this attribute set to <literal>true</literal>, it will scan the content of
<filename>META-INF/services</filename> and add the discovered services to a registry
(Note that the registry bundle supports other ways to declare SPI providers,
but this is not really relevant for the present discussion).
</para>
</listitem>
<listitem>
<para>
The <classname>ContextFinder</classname> class (the interface of which is defined by
the JAXB specification and that is used by the <methodname>newInstance</methodname>
method in <classname>JAXBContext</classname>) in the API bundle delegates the discovery
of the SPI implementation to a static method of the <classname>ProviderLocator</classname>
class defined by the locator bundle (which is not specific to JAXB and is used by other
API bundles as well). This is true both in an OSGi environment and in a non OSGi environment.
</para>
<para>
The build is configured (using a <literal>Private-Package</literal> instruction)
such that the classes of the locator bundle are actually included into the API bundle, thus
avoiding an additional dependency.
</para>
</listitem>
<listitem>
<para>
The <classname>ProviderLocator</classname> class and related code provided by the locator bundle is designed
such that in a non OSGi environment, it will simply use JDK 1.3 service discovery to locate
the SPI implementation, without ever loading any OSGi specific class. On the other hand,
in an OSGi environment, it will query the registry maintained by the registry bundle to locate
the provider. The reference to the registry is injected into the <classname>ProviderLocator</classname>
class using a bundle activator.
</para>
</listitem>
<listitem>
<para>
Finally, it should also be noted that the API bundle is configured with <literal>singleton=true</literal>.
There is indeed no meaningful way how providers could be matched with different versions of the same API
bundle.
</para>
</listitem>
</itemizedlist>
<para>
This is an example of a particularly elegant way to satisfy <xref linkend="osgi-req-no-separate-bundles"/>,
<xref linkend="osgi-req-same-api"/> and <xref linkend="osgi-req-same-impl-selection"/>, especially because
it relies on the same metadata (the <filename>META-INF/services/javax.xml.bind.JAXBContext</filename> resources)
in OSGi and non OSGi environments.
</para>
<para>
Obviously, Axiom could reuse the registry and locator bundles developed by Geronimo. This however would
contradict <xref linkend="osgi-reg-no-framework"/>. In addition, for Axiom there is no requirement to
strictly follow the JDK 1.3 service discovery algorithm. Therefore Axiom should reuse the pattern
developed by Geronimo, but not the actual implementation.
</para>
</section>
<section>
<title>New abstract APIs</title>
<para>
Application code rarely uses DOOM as the default Axiom implementation. Several downstream projects
(e.g. the Axis2/Rampart combination) use both the default (LLOM) implementation and DOOM. They select
the implementation based on the particular context. As of Axiom 1.2.12, the only way to create an object
model instance with the DOOM implementation is to use the <classname>DOOMAbstractFactory</classname> API
or to instantiate one of the factory classes (<classname>OMDOMMetaFactory</classname>, <classname>OMDOMFactory</classname>
or one of the subclasses of <classname>DOMSOAPFactory</classname>). All these classes are part of
the <literal>axiom-dom</literal> artifact. This is clearly in contradiction with <xref linkend="osgi-ref-impl-not-exported"/>
and <xref linkend="osgi-req-dropin"/>.
</para>
<para>
To overcome this problem the Axiom API must be enhanced to make it possible to select an Axiom
implementation based on capabilities/features requested by the application code. E.g. in the case
of DOOM, the application code would request a factory that implements the DOM API. It is then up
to the Axiom API classes to locate an appropriate implementation, which may be DOOM or another
drop-in replacement, as per <xref linkend="osgi-req-dropin"/>.
</para>
<para>
If multiple Axiom implementations are available (on the class path in non OSGi environment or
deployed as bundles in an OSGi environment), then the Axiom API must also be able to select an
appropriate default implementation if no specific feature is requested by the application code.
This can be easily implemented by defining a special feature called "default" that would be
declared by any Axiom implementation that is suitable as a default implementation.
</para>
<note>
<para>
DOOM is generally not considered suitable as a default implementation because it doesn't
implement the complete Axiom API (e.g. it doesn't support <classname>OMSourcedElement</classname>)
and because the factory classes are not stateless.
</para>
</note>
<para>
Finally, to make the selection algorithm deterministic, there should also be a concept
of priority: if multiple Axiom implementations are found for the same feature, then the Axiom API
would select the one with the highest priority.
</para>
<para>
This leads to the following design:
</para>
<orderedlist>
<listitem>
<para>
Every Axiom implementation declares a set of features that it supports. A feature is
simply identified by a string. Two features are predefined by the Axiom API:
</para>
<itemizedlist>
<listitem>
<para>
<literal>default</literal>: indicates that the implementation is a complete
implementation of the Axiom API and may be used as a default implementation.
</para>
</listitem>
<listitem>
<para>
<literal>dom</literal>: indicates that the implementation supports DOM
in addition to the Axiom API.
</para>
</listitem>
</itemizedlist>
<para>
For every feature it declares, the Axiom implementation specifies a priority,
which is a positive integer.
</para>
</listitem>
<listitem>
<para>
The relevant Axiom APIs are enhanced so that they take an optional argument
specifying the feature requested by the application code. If no explicit feature
is requested, then Axiom will use the <literal>default</literal> feature.
</para>
</listitem>
<listitem>
<para>
To determine the <classname>OMMetaFactory</classname> to be used, Axiom locates
the implementations declaring the requested feature and selects the one that
has the highest priority for that feature.
</para>
</listitem>
</orderedlist>
<para>
A remaining question is how the implementation declares the feature/priority information.
There are two options:
</para>
<itemizedlist>
<listitem>
<para>
Add a method to <classname>OMMetaFactory</classname> that allows the Axiom API
to query the feature/priority information from the implementation (i.e. the
features and priorities are hardcoded in the implementation).
</para>
</listitem>
<listitem>
<para>
Let the implementation provide this information declaratively in its metadata
(either in the manifest or in a separate resource with a well defined name).
Note that in a non OSGi environment, such a metadata resource must be used anyway
to enable the Axiom API to locate the <classname>OMMetaFactory</classname> implementations.
Therefore this would be a natural place to declare the features as well.
</para>
</listitem>
</itemizedlist>
<para>
The second option has the advantage to make it easier for users to debug and tweak
the implementation discovery process (e.g. there may be a need to
customize the features and priorities declared by the different implementations to ensure
that the right implementation is chosen in a particular use case).
</para>
<para>
This leads to the following design decision:
the features and priorities (together with the class name of the <classname>OMMetaFactory</classname>
implementation) will be defined in an XML descriptor with resource name <filename>META-INF/axiom.xml</filename>.
The format of that descriptor must take into account that a single JAR may contain several
Axiom implementations (e.g. if the JAR is an uber-JAR repackaged from the standard Axiom JARs).
</para>
</section>
</chapter>
<chapter>
<title>Release process</title>
<section>
<title>Release preparation</title>
<para>
The following items should be checked before starting the release process:
</para>
<itemizedlist>
<listitem>
<para>
Check for the latest Apache parent POM version (artifact <literal>org.apache:apache</literal>)
and if necessary, change the parent of the Axiom root POM.
</para>
</listitem>
<listitem>
<para>
Check the dependencies between Java packages in the <filename>axiom-api</filename> module.
The <package>org.apache.axiom.util</package> package (including its subpackages) is specified
to contain utility classes that don't depend on higher level APIs. More precisely,
<package>org.apache.axiom.util</package> should only have dependencies on
<package>org.apache.axiom.ext</package>, but not e.g. on <package>org.apache.axiom.om</package>.
<ulink url="http://www.hello2morrow.com/products/sonarj">SonarJ</ulink> can be used
to check these dependencies. The following figure shows the expected structure:
</para>
<figure>
<title>Package dependencies for r944680</title>
<mediaobject>
<imageobject>
<imagedata fileref="sonarj-944680.png" format="PNG"/>
</imageobject>
</mediaobject>
</figure>
<para>
In contrast, the following figure shows an earlier trunk version of <filename>axiom-api</filename>
with incorrect layering and cyclic dependencies involving <package>org.apache.axiom.util</package>:
</para>
<figure>
<title>Package dependencies for r939984</title>
<mediaobject>
<imageobject>
<imagedata fileref="sonarj-939984.png" format="PNG"/>
</imageobject>
</mediaobject>
</figure>
<para>
The check can also be done using <ulink url="http://mojo.codehaus.org/jdepend-maven-plugin/">jdepend-maven-plugin</ulink>.
To do this, execute the following command in the <filename>axiom-api</filename> module:
</para>
<screen>mvn jdepend:generate</screen>
<para>
Then open <filename>target/site/jdepend-report.html</filename> and go the the "Cycles" section.
The report should not show any package cycles involving <package>org.apache.axiom.mime</package>,
<package>org.apache.axiom.util</package> and <package>org.apache.axiom.ext</package>.
</para>
</listitem>
<listitem>
<para>
Check that the generated Javadoc contains the appropriate set of packages.
In particular, unit test related classes should be excluded, except for the test suite
classes in <package>org.apache.axiom.ts</package> (we don't need to hide the fact that we have
a reusable test suite...).
</para>
</listitem>
<listitem>
<para>
Check that all dependencies and plugins are available from standard
repositories. To do this, clean the local repository and execute
<command>mvn clean install</command> followed by <command>mvn site</command>.
</para>
</listitem>
<listitem>
<para>
Check that the set of license files in the <filename>legal</filename> directory
is complete and accurate.
</para>
</listitem>
<listitem>
<para>
Check that the Maven site conforms to the latest version of the
<ulink url="http://apache.org/foundation/marks/pmcs">Apache Project
Branding Guidelines</ulink>.
</para>
</listitem>
<listitem>
<para>
Check that the <literal>apache-release</literal> profile can be executed properly.
To do this, issue the following command:
</para>
<screen>mvn clean install -Papache-release -Dmaven.test.skip=true</screen>
<para>
You may also execute a dry run of the release process:
</para>
<screen>mvn release:prepare -DdryRun=true</screen>
<para>
After this, you need to clean up using the following command:
</para>
<screen>mvn release:clean</screen>
</listitem>
<listitem>
<para>
Prepare the release note. This should include a description of the major
changes in the release as well as a list of resolved JIRA issues. Note that
both <filename>index.apt</filename> and <filename>RELEASE-NOTE.txt</filename>
need to be updated.
</para>
</listitem>
<listitem>
<para>
Add an entry for the release to the <filename>download.xml.vm</filename> file and
change the links for older releases so that they point to <literal>archive.apache.org</literal>
(Since the Axiom project doesn't use branches and produces releases directly from the trunk,
there should only be a single mirrored release).
</para>
</listitem>
<listitem>
<para>
Preview and validate the changes that will be done by the release plugin
to the POM files. In order to do this, execute the following command:
</para>
<screen>mvn release:prepare -DdryRun=true -Dmaven.test.skip=true</screen>
<para>
Next, compare the <filename>pom.xml.tag</filename> files to the corresponding
<filename>pom.xml</filename> files:
</para>
<screen>for pom in $(find . -name "pom.xml"); do diff $pom $pom.tag; done</screen>
<para>
The differences should be limited to <sgmltag class="element">version</sgmltag>
and <sgmltag class="element">scm</sgmltag> tags. If necessary, change the
original POM files to avoid spurious changes. After that, clean up using:
</para>
<screen>mvn release:clean</screen>
</listitem>
</itemizedlist>
</section>
<section>
<title>Prerequisites</title>
<para>
The following things are required to perform the actual release:
</para>
<itemizedlist>
<listitem>
<para>
A PGP key that conforms to the <ulink url="http://www.apache.org/dev/release-signing.html">requirement
for Apache release signing</ulink>. To make the release process easier, the passphrase for the
code signing key should be configured in <filename>${user.home}/.m2/settings.xml</filename>:
</para>
<screen><![CDATA[<settings>
...
<profiles>
<profile>
<id>apache-release</id>
<properties>
<gpg.passphrase> <!-- YOUR KEY PASSPHRASE --> </gpg.passphrase>
</properties>
</profile>
</profiles>
...
</settings>]]></screen>
</listitem>
<listitem>
<para>
The release process uses a Nexus staging repository. Every committer should have access to the corresponding
staging profile in Nexus. To validate this, login to <literal>repository.apache.org</literal> and check that
you can see the <literal>org.apache.ws</literal> staging profile. The credentials used to deploy to Nexus
should be added to <filename>settings.xml</filename>:
</para>
<screen><![CDATA[<servers>
...
<server>
<id>apache.releases.https</id>
<username><!-- ASF username --></username>
<password><!-- ASF LDAP password --></password>
</server>
...
</servers>]]></screen>
</listitem>
</itemizedlist>
</section>
<section>
<title>Release</title>
<para>
In order to prepare the release artifacts for vote, execute the following steps:
</para>
<procedure>
<step>
<para>
Update the release date in <filename>download.xml.vm</filename> and <filename>index.apt</filename>.
</para>
</step>
<step>
<para>
Temporarily disable the Jenkins build(s) for Axiom, in order to avoid accidental
deployment of the release candidate to the local repository of a Jenkins executor
if the release process fails somewhere in the middle and/or a Jenkins build
starts at the wrong moment.
</para>
</step>
<step>
<para>
Start the release process with the following command:
</para>
<screen>mvn release:prepare</screen>
<para>
When asked for the "SCM release tag or label", override the default value
(<literal>axiom-x.y.z</literal>) by entering a tag in the form
<literal>x.y.z</literal>, which is compatible with the tag names used for
previous releases.
</para>
<para>
The above command will create a tag in Subversion and increment the version
number of the trunk to the next development version. It will also create
a <filename>release.properties</filename> file that will be used in the next step.
</para>
</step>
<step>
<para>
Perform the release using the following command:
</para>
<screen>mvn release:perform</screen>
<para>
This will upload the release artifacts to the Nexus staging repository.
</para>
</step>
<step>
<para>
Log in to the Nexus repository (<ulink url="https://repository.apache.org/"/>
and close the staging repository. The name of the staging profile is
<literal>org.apache.ws</literal>. See <ulink url="http://maven.apache.org/developers/release/apache-release.html"/>
for a more thorough description of this step.
</para>
</step>
<step>
<para>
Generate and deploy the Maven site on a public Web server. You may use
<literal>people.apache.org</literal> for this.
</para>
</step>
<step>
<para>
Start the release vote by sending a mail to <literal>dev@ws.apache.org</literal>.
The mail should mention the following things:
</para>
<itemizedlist>
<listitem>
<para>
The list of issues solved in the release (by linking to the relevant
JIRA view).
</para>
</listitem>
<listitem>
<para>
The location of the Nexus staging repository.
</para>
</listitem>
<listitem>
<para>
The location where source and binary packages may be downloaded.
This can be a reference to the location inside the staging repository.
</para>
</listitem>
<listitem>
<para>
A link to the preview of the Maven site.
</para>
</listitem>
</itemizedlist>
</step>
<step>
<para>
Reenable the Jenkins build(s).
</para>
</step>
</procedure>
<para>
If the vote passes, execute the following steps:
</para>
<procedure>
<step>
<para>
Promote the artifacts in the staging repository. See
<ulink url="http://maven.apache.org/developers/release/apache-release.html"/>
for detailed instructions for this step.
</para>
</step>
<step>
<para>
Log in to <literal>people.apache.org</literal> and publish the release
distributions to <literal>www.apache.org</literal>. The <filename>etc/dist.py</filename>
script can be used for that:
</para>
<screen>cd /www/www.apache.org/dist/ws/commons/axiom
umask 0002
python <replaceable>path_to_etc</replaceable>/dist.py <replaceable>version</replaceable></screen>
<para>
<replaceable>version</replaceable> is the release version, e.g. <literal>1.2.9</literal>.
</para>
<para>
If not yet done, export your public key and append it to the <filename>KEYS</filename>
file located in <filename>/www/www.apache.org/dist/ws/commons/axiom</filename>. The command
to export a public key is as follows:
</para>
<screen>gpg --armor --export <replaceable>key_id</replaceable></screen>
</step>
<step>
<para>
Check out the release tag from Subversion and generate the Maven site.
Also check out the existing site:
</para>
<screen>svn co https://svn.apache.org/repos/asf/webservices\
/axiom/site axiom-site</screen>
<para>
The existing site needs to be overwritten with the site generated for the new
release. This can be done easily using the <filename>etc/syncsite.py</filename> script
(requires Python 2.6). It will copy the files and execute any necessary Subversion
commands to add new files and to delete files that no longer exist. After executing the
script, commit the changes to Subversion.
</para>
<important>
<para>
When generating the site, please use a recent JDK version, because some older versions
generate broken links.
</para>
</important>
</step>
<step>
<para>
Log in to <literal>people.apache.org</literal> and update the site:
</para>
<screen>cd /www/ws.apache.org/axiom/
umask 0002
svn update</screen>
<para>
The umask setting makes sure that other members of the ws group will be able
to update the site as well.
</para>
</step>
</procedure>
<para>
It may take several hours before all the updates have been synchronized to the relevant
ASF systems. Before proceeding, check that
</para>
<itemizedlist>
<listitem>
<para>
the Maven artifacts for the release are available from the Maven central repository;
</para>
</listitem>
<listitem>
<para>
the Maven site has been synchronized to <ulink url="http://ws.apache.org/axiom/"/>;
</para>
</listitem>
<listitem>
<para>
the binary and source distributions can be downloaded from <ulink url="http://ws.apache.org/axiom/download.cgi"/>.
</para>
</listitem>
</itemizedlist>
<para>
Once everything is in place, send announcements to <literal>users@ws.apache.org</literal>
and <literal>announce@apache.org</literal>. Since the two lists have different conventions, audiences
and moderation policies, to send the announcement separately to the two lists.
</para>
<para>
Sample announcement:
</para>
<blockquote>
<para>
Apache Axiom Team is pleased to announce the release of Axiom x.y.z. The release is available
for download at:
</para>
<para>
http://ws.apache.org/axiom/download.cgi
</para>
<para>
Apache Axiom is a StAX-based, XML Infoset compliant object model which supports on-demand building
of the object tree. It supports a novel "pull-through" model which allows one to turn off the tree
building and directly access the underlying pull event stream. It also has built in support for
XML Optimized Packaging (XOP) and MTOM, the combination of which allows XML to carry binary
data efficiently and in a transparent manner. The combination of these is an easy to use API
with a very high performant architecture!
</para>
<para>
Developed as part of Apache Axis2, Apache Axiom is the core of Apache Axis2. However, it is a
pure standalone XML Infoset model with novel features and can be used independently of Apache Axis2.
</para>
<para>
Highlights in this release:
</para>
<itemizedlist>
<listitem>
<para>
...
</para>
</listitem>
<listitem>
<para>
...
</para>
</listitem>
</itemizedlist>
<para>
Resolved JIRA issues:
</para>
<itemizedlist>
<listitem>
<para>
[WSCOMMONS-513] Behavior of insertSiblingAfter and insertSiblingBefore is not well defined for orphan nodes
</para>
</listitem>
<listitem>
<para>
[WSCOMMONS-488] The sequence of events produced by OMStAXWrapper with inlineMTOM=false is inconsistent
</para>
</listitem>
</itemizedlist>
</blockquote>
<para>
For <literal>users@ws.apache.org</literal>, the subject (<quote>Axiom x.y.z released</quote>) should be
prefixed with <quote>[ANN][Axiom]</quote>, while for <literal>announce@apache.org</literal>
<quote>[ANN]</quote> is enough. Note that mail to <literal>announce@apache.org</literal> must be
sent from an <literal>apache.org</literal> address.
</para>
</section>
<section>
<title>Post-release actions</title>
<itemizedlist>
<listitem>
<para>
Update the DOAP file (see <filename>etc/axiom.rdf</filename>) and add a new entry
for the release.
</para>
</listitem>
<listitem>
<para>
Update the status of the release version in the AXIOM project in JIRA.
</para>
</listitem>
<listitem>
<para>
Remove archived releases from <filename>/www/www.apache.org/dist/ws/commons/axiom</filename>
on <literal>people.apache.org</literal>.
</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>References</title>
<para>
The following documents are useful when preparing and executing the release:
</para>
<itemizedlist>
<listitem>
<para>
<ulink url="http://www.apache.org/legal/src-headers.html">ASF Source Header and Copyright Notice Policy</ulink>
</para>
</listitem>
<listitem>
<para>
<ulink url="http://apache.org/foundation/marks/pmcs">Apache Project Branding Guidelines</ulink>
</para>
</listitem>
<listitem>
<para>
<ulink url="http://projects.apache.org/doap.html">DOAP Files</ulink>
</para>
</listitem>
<listitem>
<para>
<ulink url="http://www.apache.org/dev/release-publishing.html">Publishing Releases</ulink>
</para>
</listitem>
</itemizedlist>
</section>
</chapter>
<chapter>
<title>The StAX specification</title>
<para>
The StAX specification comprises two parts: a specification document titled <quote>Streaming API
For XML JSR-173 Specification</quote> and a Javadoc describing the API. Both can be downloaded from the
<ulink url="http://jcp.org/en/jsr/detail?id=173">JSR-173 page</ulink>. Since StAX is part of Java 6,
the Javadocs can also be viewed
<ulink url="http://java.sun.com/javase/6/docs/api/javax/xml/stream/package-summary.html">online</ulink>.
</para>
<section>
<title>Semantics of the <methodname>setPrefix</methodname> method</title>
<para>
Probably one of the more obscure parts of the StAX specifications is the meaning of the
<methodname>setPrefix</methodname><footnote><para>For simplicity, we only discuss
<methodname>setPrefix</methodname> here. The same remarks also apply to
<methodname>setDefaultNamespace</methodname>.</para></footnote> method defined by <classname>XMLStreamWriter</classname>.
To understand how this method works, it is necessary to look at different parts of the specification:
</para>
<itemizedlist>
<listitem>
<para>
The Javadoc of the <methodname>setPrefix</methodname> method.
</para>
</listitem>
<listitem>
<para>
The table shown in the Javadoc of the <classname>XMLStreamWriter</classname> class
in Java 6<footnote><para>This table is not included in the Javadoc in the original StAX
specification.</para></footnote>.
</para>
</listitem>
<listitem>
<para>
Section 5.2.2, <quote>Binding Prefixes</quote> of the specification.
</para>
</listitem>
<listitem>
<para>
The example shown in section 5.3.2, <quote>XMLStreamWriter</quote> of the specification.
</para>
</listitem>
</itemizedlist>
<para>
In addition, it is important to note the following facts:
</para>
<itemizedlist>
<listitem>
<para>
The terms <firstterm>defaulting prefixes</firstterm> used in section 5.2.2 of the
specification and <firstterm>namespace repairing</firstterm> used in the Javadocs
of <classname>XMLStreamWriter</classname> are synonyms.
</para>
</listitem>
<listitem>
<para>
The methods writing namespace qualified information items, i.e.
<methodname>writeStartElement</methodname>, <methodname>writeEmptyElement</methodname>
and <methodname>writeAttribute</methodname> all come in two variants: one that
takes a namespace URI and a prefix as arguments and one that only takes a
namespace URI, but no prefix.
</para>
</listitem>
</itemizedlist>
<para>
The purpose of the <methodname>setPrefix</methodname> method is simply to define the prefixes that
will be used by the variants of the <methodname>writeStartElement</methodname>,
<methodname>writeEmptyElement</methodname> and <methodname>writeAttribute</methodname> methods
that only take a namespace URI (and the local name). This becomes clear by looking at the
table in the <classname>XMLStreamWriter</classname> Javadoc. Note that a call to
<methodname>setPrefix</methodname> doesn't cause any output and it is still necessary
to use <methodname>writeNamespace</methodname> to actually write the necessary
namespace declarations. Otherwise the produced document will not be well formed with
respect to namespaces.
</para>
<para>
The Javadoc of the <methodname>setPrefix</methodname> method also clearly defines the scope
of the prefix bindings defined using that method: a prefix bound using
<methodname>setPrefix</methodname> remains valid till the invocation of
<methodname>writeEndElement</methodname> corresponding to the last invocation of
<methodname>writeStartElement</methodname>. While not explicitly mentioned in the
specifications, it is clear that a prefix binding may be masked by another binding
for the same prefix defined in a nested element.
</para>
<para>
An aspect that may cause confusion is the fact that in the example shown in section
5.3.2 of the specifications, the calls to <methodname>setPrefix</methodname> (and
<methodname>setDefaultNamespace</methodname>) all appear immediately before a
call to <methodname>writeStartElement</methodname> or <methodname>writeEmptyElement</methodname>.
This may lead people to incorrectly believe that a prefix binding defined using
<methodname>setPrefix</methodname> only applies to the next element
written<footnote><para>Another factor that contributes to the confusion is that in SAX,
prefix mappings are always generated before the corresponding <methodname>startElement</methodname>
event and that their scope ends with the corresponding <methodname>endElement</methodname>
event. This is so because the <classname>ContentHandler</classname> interface specifies that
<quote>all <methodname>startPrefixMapping</methodname> events will occur immediately before the
corresponding <methodname>startElement</methodname> event, and all <methodname>endPrefixMapping</methodname>
events will occur immediately after the corresponding <methodname>endElement</methodname>
event</quote>.</para></footnote>.
This interpretation is clearly in contradiction with the <methodname>setPrefix</methodname>
Javadoc, unless one assumes that <quote>the current START_ELEMENT / END_ELEMENT pair</quote>
means the element opened by a call to <methodname>writeStartElement</methodname> immediately following
the call to <methodname>setPrefix</methodname>. This however would be a very arbitrary interpretation
of the Javadoc<footnote><para>Early versions of XL XP-J were based on this interpretation of the
specifications, but this has been corrected. Versions conforming to the specifications support
a special property called <varname>javax.xml.stream.XMLStreamWriter.isSetPrefixBeforeStartElement</varname>,
which always returns <code>Boolean.FALSE</code>. This allows to easily distinguish the non
conforming versions from the newer versions. Note that in contrast to what the usage of the reserved
<literal>javax.xml.stream</literal> prefix suggests, this is a vendor specific property that
is not supported by other implementations.</para></footnote>.
</para>
<para>
The correctness of the comments in the previous paragraph can be checked using the following
code snippet:
</para>
<programlisting>XMLOutputFactory f = XMLOutputFactory.newInstance();
XMLStreamWriter writer = f.createXMLStreamWriter(System.out);
writer.writeStartElement("root");
writer.setPrefix("p", "urn:ns1");
writer.writeEmptyElement("urn:ns1", "element1");
writer.writeEmptyElement("urn:ns1", "element2");
writer.writeEndElement();
writer.flush();
writer.close();</programlisting>
<para>
This produces the following output<footnote><para>This has been tested with
Woodstox 3.2.9, SJSXP 1.0.1 and version 1.2.0 of the reference
implementation.</para></footnote>:
</para>
<screen><![CDATA[<root><p:element1/><p:element2/></root>]]></screen>
<para>
Since the code doesn't call <methodname>writeNamespace</methodname>, the output is obviously not
well formed with respect to namespaces, but it also clearly shows that the scope of the
prefix binding for <literal>p</literal> extends to the end of the
<sgmltag class="element">root</sgmltag> element and is not limited to
<sgmltag class="element">element1</sgmltag>.
</para>
<para>
To avoid unexpected results and keep the code maintainable, it is in general advisable to keep
the calls to <methodname>setPrefix</methodname> and <methodname>writeNamespace</methodname> aligned,
i.e. to make sure that the scope (in <classname>XMLStreamWriter</classname>) of the prefix binding
defined by <methodname>setPrefix</methodname> is compatible with the scope (in the produced
document) of the namespace declaration written by the corresponding call
to <methodname>writeNamespace</methodname>. This makes it necessary to write code like this:
</para>
<programlisting>writer.writeStartElement("p", "element1", "urn:ns1");
writer.setPrefix("p", "urn:ns1");
writer.writeNamespace("p", "urn:ns1");</programlisting>
<para>
As can be seen from this code snippet, keeping the two scopes in sync makes it necessary to use
the <methodname>writeStartElement</methodname> variant which takes an explicit prefix. Note that
this somewhat conflicts with the purpose of the <methodname>setPrefix</methodname> method;
one may consider this as a flaw in the design of the StAX API.
</para>
</section>
<section>
<title>The three <classname>XMLStreamWriter</classname> usage patterns</title>
<para>
Drawing the conclusions from the previous section and taking into account that
<classname>XMLStreamWriter</classname> also has a <quote>namespace repairing</quote>
mode, one can see that there are in fact three different ways to use
<classname>XMLStreamWriter</classname>. These usage patterns correspond to the
three bullets in section 5.2.2 of the StAX specification<footnote><para>The content
of this section is largely based on a <ulink url="http://markmail.org/message/olsdl3p3gciqqeob">reply
posted by Tatu Saloranta on the Axiom mailing list</ulink>. Tatu is the main developer of the
Woodstox project.</para></footnote>:
</para>
<orderedlist>
<listitem>
<para>
In the <quote>namespace repairing</quote> mode (enabled by the
<varname>javax.xml.stream.isRepairingNamespaces</varname> property), the writer
takes care of all namespace bindings and declarations, with minimal help from
the calling code. This will always produce output that is well-formed with respect
to namespaces. On the other hand, this adds some overhead and the result may
depend on the particular StAX implementation (though the result produced by
different implementations will be equivalent).
</para>
<para>
In repairing mode the calling code should avoid writing namespaces explicitly
and leave that job to the writer. There is also no need to call
<methodname>setPrefix</methodname>, except to suggest a preferred prefix for
a namespace URI. All variants of <methodname>writeStartElement</methodname>,
<methodname>writeEmptyElement</methodname> and <methodname>writeAttribute</methodname>
may be used in this mode, but the implementation can choose whatever prefix mapping
it wants, as long as the output results in proper URI mapping for elements and
attributes.
</para>
</listitem>
<listitem>
<para>
Only use the variants of the writer methods that take an explicit prefix together
with the namespace URI. In this usage pattern, <methodname>setPrefix</methodname>
is not used at all and it is the responsibility of the calling code to keep
track of prefix bindings.
</para>
<para>
Note that this approach is difficult to implement when different parts of the output document
will be produced by different components (or even different libraries). Indeed, when
passing the <classname>XMLStreamWriter</classname> from one method or component
to the other, it will also be necessary to pass additional information about the
prefix mappings in scope at that moment, unless the it is acceptable to let the
called method write (potentially redundant) namespace declarations for all namespaces
it uses.
</para>
</listitem>
<listitem>
<para>
Use <methodname>setPrefix</methodname> to keep track of prefix bindings and make sure that
the bindings are in sync with the namespace declarations that have been written,
i.e. always use <methodname>setPrefix</methodname> immediately before or immediately
after each call to <methodname>writeNamespace</methodname>. Note that the code is
still free to use all variants of <methodname>writeStartElement</methodname>,
<methodname>writeEmptyElement</methodname> and <methodname>writeAttribute</methodname>;
it only needs to make sure that the usage it makes of these methods is consistent with
the prefix bindings in scope.
</para>
<para>
The advantage of this approach is that it allows to write modular code: when a
method receives an <classname>XMLStreamWriter</classname> object (to write
part of the document), it can use
the namespace context of that writer (i.e. <methodname>getPrefix</methodname>
and <methodname>getNamespaceContext</methodname>) to determine which namespace
declarations are currently in scope in the output document and to avoid
redundant or conflicting namespace declarations. Note that in order to do so,
such code will have to check for an existing prefix binding before starting
to use a namespace.
</para>
</listitem>
</orderedlist>
</section>
</chapter>
<appendix>
<title>Appendix</title>
<section id="install.ibm.jdk">
<title>Installing IBM's JDK on Debian Linux</title>
<procedure>
<step>
<para>
Make sure that <literal>fakeroot</literal> and <literal>java-package</literal>
are installed:
</para>
<screen># <userinput>apt-get install fakeroot java-package</userinput></screen>
</step>
<step>
<para>
Download the <filename>.tgz</filename> version of the JDK from
<ulink url="http://www.ibm.com/developerworks/java/jdk/linux/download.html"/>.
</para>
</step>
<step>
<para>
Edit <filename>/usr/share/java-package/ibm-j2sdk.sh</filename> and (if necessary)
add an entry for the particular version of the IBM JDK downloaded in the previous
step.
</para>
</step>
<step>
<para>
Build a Debian package from the tarball:
</para>
<screen>$ <userinput>fakeroot make-jpkg <replaceable>xxxx</replaceable>.tgz</userinput></screen>
</step>
<step>
<para>
Install the Debian package.
</para>
</step>
</procedure>
</section>
</appendix>
</book>