blob: 493d7228029557dd2fb2e1fb0f303de31e448b50 [file] [log] [blame]
<?xml version="1.0" encoding="utf-8"?>
<!-- $Id$ -->
<!--
Copyright 2004 The Apache Software Foundation
Licensed 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.
-->
<!DOCTYPE book PUBLIC
"-//OASIS//DTD DocBook XML V4.1.2//EN"
"http://www.oasis-open.org/docbook/xml/4.0/docbookx.dtd" [
<!ENTITY % TapestryLinks SYSTEM "../common/TapestryLinks.xml">
%TapestryLinks;
]>
<!-- Conventions:
Component ids are <varname>
Java packages and class names are <classname>
Tapestry component aliases are <classname>
In-line code snippets use <function>
Property paths and JavaBeans property names used <varname>
-->
<book>
<title>Tapestry Contributor's Guide</title>
<bookinfo>
<author>
<firstname>Howard</firstname>
<surname>Lewis Ship</surname>
</author>
<copyright>
<year>2002</year>
<year>2003</year>
<holder>The Apache Software Foundation</holder>
</copyright>
</bookinfo>
<chapter id="intro">
<title>Introduction</title>
<para>
This document is a guide to developers who want to go
beyond merely developing applications <emphasis>using</emphasis> Tapestry,
and want to extend and improve Tapestry itself.
</para>
<para>
Tapestry has benefitted over the first two years of its
development from having a focused vision and, predominantly,
a single developer. At the time of this writing, May 2002,
the Tapestry community is truly coming alive, with
new developers contributing fixes, components and documentation.
</para>
<para>
The goal is to maintain the stability of Tapestry even as it
shifts from a one-man-show to a true community effort. Meanwhile
it is vitally important to not to sacrifice quality in either
code or <emphasis>documentation</emphasis> if Tapestry is to
stay on track.
</para>
<para>
Contributing to Tapestry requires a commitment to produce excellent code,
examples and documentation. In fact, proper documentation in JavaDoc and
as updates to the tutorials and manuals represents the
<emphasis>dominant</emphasis> amount of effort when contributing to Tapestry.
</para>
</chapter>
<chapter id="cvs">
<title>CVS Access</title>
<para>
Using Eclipse, obtaining the source code takes only a few steps. Tapestry
compiles using some libraries from &JBoss; 3.0.6 and &Jetty; 4.x which must be downloaded first.
</para>
<para>
Eclipse must be configured with the location of JBoss, this is done from the preferences
panel. A new entry for <literal>JBOSS_DIR</literal> should be added.
</para>
<figure>
<title>Eclipse: Java Classpath Preferences</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/eclipse-classpath.png" format="PNG"/>
</imageobject>
</mediaobject>
</figure>
<para>
Activate the CVS Repositories view and use the context menu to create a new CVS Repository location.
This raises a panel for defining connection information. Fill in your own
Jakarta name and password:
</para>
<figure>
<title>Eclipse: New CVS Repository Location</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/eclipse-add-repository.png" format="PNG"/>
</imageobject>
</mediaobject>
</figure>
<para>
Next, open the new CVS Repository location. Expand the "HEAD" node, then scroll down to the "jakarta-tapestry" module.
Right click and select "Check Out As Project".
</para>
<figure>
<title>Eclipse: Check Out Project</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/eclipse-check-out-project.png" format="PNG"/>
</imageobject>
</mediaobject>
</figure>
<para>
Eclipse will checkout the latest versions of all the Tapestry code and compile it.
</para>
<para>
You can access the Tapestry repository using command line CVS or other tools, as well. Details
for using command line CVS are available at the &Jakarta;.
</para>
</chapter>
<chapter id="building">
<title>Building Tapestry</title>
<para>
Tapestry is built using &Ant; 1.5. In addition,
Tapestry includes the necessary control files to allow
development using the excellent open-source IDE, &Eclipse;.
</para>
<para>
To perform a full build from the command line, you must have
JDK 1.3 or better installed, as well
as &JBoss; 3.0.6.
</para>
<para>
You must create the file <filename>config/build.properties</filename>
(under the Tapestry root directory). This file defines a property,
<literal>jboss.dir</literal> that identifies the full pathname
to the JBoss installation and the &Jetty; installation. A sample file is provided.
<tip>
<para>
Be sure to use forward slashes for the path name, even
under Windows. Using backslashes, the escape character
in property files, will cause the build to fail, since
Ant will be using incorrect paths to the libraries obtained
from the JBoss distribution.
</para>
</tip>
</para>
<section id="building.subprojects">
<title>Tapestry Subprojects</title>
<para>
The Tapestry source tree contains multiple sub-projects, each in its own subdirectory,
with its own Ant build file and own source code tree.
A root level build file (described in the <link linkend="building.targets">next section</link>)
performs builds over all sub-projects.
</para>
<variablelist>
<title>Tapestry Sub-Projects</title>
<varlistentry>
<term><filename>framework</filename></term>
<listitem>
<para>
Contains the core framework, builds &TapestryFrameworkJar;.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>contrib</filename></term>
<listitem>
<para>
Builds &TapestryContribJar;.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>junit</filename></term>
<listitem>
<para>
Builds and runs JUnit tests.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>examples/Workbench</filename></term>
<listitem>
<para>
Builds <filename>workbench.war</filename>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>examples/VlibBeans</filename></term>
<listitem>
<para>
Builds <filename>vlibbeans.jar</filename>, the EJBs used
by the Virtual Library demonstration.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>examples/Vlib</filename></term>
<listitem>
<para>
Builds <filename>vlib.war</filename>, the presentation
layer of the Virtual Library demonstration.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>examples/VlibEAR</filename></term>
<listitem>
<para>
Builds <filename>vlib.ear</filename> from
<filename>vlibbeans.jar</filename> and
<filename>vlib.war</filename>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>doc/src/DevelopersGuide</filename></term>
<listitem>
<para>
Builds the Tapestry Developer's Guide documentation.
This guide is out of date, as is being replaced.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>doc/src/UsersGuide</filename></term>
<listitem>
<para>
Builds the Tapestry Users' Guide (the replacement for the Developer's
Guide). This document is still incomplete. See, you just can't win.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>doc/src/ContributorsGuide</filename></term>
<listitem>
<para>
Builds this very documentation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>doc/src/ComponentReference</filename></term>
<listitem>
<para>
Builds the component reference documentation.
</para>
</listitem>
</varlistentry>
</variablelist>
</section>
<section id="building.targets">
<title>Build Targets</title>
<para>
The following Ant build targets are available from the Tapestry root directory:
<variablelist>
<title>Root Targets</title>
<varlistentry>
<term>clean</term>
<listitem>
<para>Cleans each sub-project and deletes derived files (such as
the Tapestry framework JAR and examples).</para>
</listitem>
</varlistentry>
<varlistentry>
<term>clean-all</term>
<listitem>
<para>
As with <literal>clean</literal>, but also deletes
all documentation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>documentation</term>
<listitem>
<para>
Builds all documentation (see notes below).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>install</term>
<listitem>
<para>
Performs a full build, by re-invoking <literal>install</literal>
in each sub-project.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>javadoc</term>
<listitem>
<para>
Creates Tapestry API documentation.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>junit</term>
<listitem>
<para>
Runs all &JUnit; tests.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>clover</term>
<listitem>
<para>
Runs all &JUnit; tests and builds a code coverage report
(using the Clover tool).
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</section>
<section id="building.doc-setup">
<title>Documentation Setup</title>
<para>
Tapestry documentation, including this manual, is also generated using Ant. Documentation
source is in &DocBook; XML format, and uses XSL transformation to generate readable HTML.
Tapestry uses &Saxon; to generate HTML documentation, and
&Fop; to generate PDF documentation.
<itemizedlist>
<listitem>
<para>
Download and unpack the &Saxon; distribution, release 6.5.2 exactly (later versions do not work).
</para>
</listitem>
<listitem>
<para>
Obtain the latest copies of the two DocBook distributions and place the files
in the <filename>ext-dist</filename> directory.
Details are in the file
<filename>doc/src/common/Readme.html</filename>.
</para>
</listitem>
<listitem>
<para>
Copy <filename>saxon.jar</filename> into the
Ant <filename>lib</filename> directory.
</para>
</listitem>
<listitem>
<para>
Update your
<envar>ANT_OPTS</envar> environment variable
to add the following two system properties:
<itemizedlist>
<listitem>
<para>
-Djavax.xml.parsers.DocumentBuilderFactory=org.apache.crimson.jaxp.DocumentBuilderFactoryImpl
</para>
</listitem>
<listitem>
<para>
-Djavax.xml.parsers.SAXParserFactory=org.apache.crimson.jaxp.SAXParserFactoryImpl
</para>
</listitem>
</itemizedlist>
</para>
</listitem>
<listitem>
<para>
Download &Fop; 0.20.4 and unpack into a permanent directory.
</para>
</listitem>
<listitem>
<para>
Update <filename>config/build.properties</filename> and add
a <literal>fop.dir</literal> entry, identifying the directory
into which you unpacked FOP. Be sure to use an absolute
path name, and only forward slashes.
</para>
</listitem>
<listitem>
<para>
Get a copy of <ulink url="http://java.sun.com/products/jimi/">JIMI</ulink>
(an imaging package from Sun, needed by FOP to process PNG image files), and
unpack it to temporary directory.
</para>
</listitem>
<listitem>
<para>
Copy <filename>JimiProClasses.zip</filename> into the <filename><replaceable>FOP</replaceable>/lib</filename>
directory.
</para>
</listitem>
</itemizedlist>
</para>
</section>
<section id="building.clover-setup">
<title>Clover Setup</title>
<para>
<ulink url="http://www.thecortex.net/clover">Clover</ulink> is a properietary tool
that gathers code coverage information and generates reports from it. They
have kindly donated a license for Clover to the Tapestry project.
</para>
<para>
To configure for clover:
<itemizedlist>
<listitem>
<para>
Get a copy of the Clover distribution.
<ulink url="http://www.thecortex.net/clover/">Cortex eBusiness</ulink> has donated a copy of Clover to
support Tapestry. The distribution is
available from
&HowardLewisShipEmail;.
</para>
</listitem>
<listitem>
<para>Extract the Clover distribution to a non-temporary directory.
</para>
</listitem>
<listitem>
<para>
Modify <filename>config/build.properties</filename> and add an entry
for <literal>clover.dir</literal>. As usual, provide the absolute
pathname to the Clover directory, using only forward slashes.
</para>
</listitem>
<listitem>
<para>
Copy <filename>clover.jar</filename> to the
<filename><replaceable>Ant</replaceable>/lib</filename> directory.
</para>
</listitem>
</itemizedlist>
</para>
<para>
The Clover report executes from the <filename>junit</filename> directory, using the Ant
target <literal>clover</literal>. It builds the clover-enhanced version of the framework
classes, and executes the JUnit test suite twice (with all logging enabled and
then with all logging disabled),
then generates the HTML report into the
<filename>web/doc/clover</filename> directory.
</para>
</section>
</chapter>
<chapter id="standards">
<title>Development Standards</title>
<para>
This chapter covers a number of standards, both in code and in procedure, expected by Tapestry contributors.
</para>
<section id="standards.id-symbol">
<title>Use of &dollar;Id$ Symbol</title>
<para>
Every file checked into the CVS repository should have the &dollar;Id$ symbol inside a comment, near the top
of the file. The <literal>&dollar;Id$</literal> token is expanded by CVS into a useful header, identifying the revision
of the file, date last changed, and name of last user to change the file.
</para>
<para>
For example, the &dollar;Id$ for this document is <literal>$Id$</literal>.
</para>
</section>
<section id="standards.typecomment">
<title>Type Comment</title>
<para>
Each Java file <emphasis>must</emphasis> have a complete and useful type comment. Type comments
must come after all <literal>import</literal> statements, and before the start of the class.
</para>
<figure>
<title>Type Comment</title>
<programlisting>
/**
* <replaceable>A useful description of the class or interface, especially covering</replaceable>
* <replaceable>how it is used, and what other classes or interfaces it interacts with.</replaceable>
*
* @author <replaceable>Your Name</replaceable>
* @version &dollar;Id$
* @since <replaceable>Version</replaceable>
*/
</programlisting>
</figure>
<para>
The <replaceable>Version</replaceable> should be replaced with the numeric version number of
the Tapestry release the type will first appear in. This is the
<link linkend="releases">minor release number</link>; for example,
a change introduced in release <literal>2.3-beta-3</literal>
would be identified as <literal>2.3</literal>.
</para>
</section>
<section id="standards.javadoc">
<title>JavaDoc</title>
<para>
All methods should be commented, with the following exceptions:
<itemizedlist>
<listitem>
<para>
Simple accessor methods with no side-effects.
</para>
</listitem>
<listitem>
<para>
Methods that are fully described by an interface and
don't add any additional behaviors.
</para>
</listitem>
</itemizedlist>
</para>
<para>
Parameters and return values should be identified. <literal>@throws</literal>
should identify when any checked exceptions are thrown; additional
<literal>@throws</literal>
entries should describe any runtime exceptions that may also be thrown.
</para>
<para>
Methods should always include a <literal>@since</literal> entry, unless the method
was added as part of a new Java class or interface, in which case
the <literal>@since</literal> for the containing type is sufficient.
Use the same version number as <link linkend="standards.typecomment">type comments</link>
when adding individual methods.
</para>
<para>
Try not to skimp on the comment (it is often best to write the comment before writing any code).
Tapestry has some of the best documentation of any open source project and that should
be maintained. Remember to try and answer the question <emphasis>why?</emphasis>, which is
always much more interesting and useful than <emphasis>how?</emphasis> or <emphasis>what?</emphasis>.
</para>
<para>
It is appropriate to create JavaDoc comments for variables, even private variables (to at least
provide an <literal>@since</literal> value).
</para>
<para>
Collections (from package <literal>java.util</literal>) should be documented to identify
the type of object stored, and for <classname>Map</classname>
the type of key. Example: <literal>List of {@link IRender}</literal>, or
<literal>Map of {@link IBinding} keyed on String name</literal>.
</para>
<para>
When a method returns a collection type, the documentation should indicate if it is safe
for the caller to modify the collection or not. In general, it is best to always return an immutable
copy of a collection, but for efficiency this is not always reasonable.
</para>
<para>
Also document any cases where a parameter is allowed to be null, or a return value may be null.
</para>
<para>
And don't forget to make liberal use of JavaDoc links (<literal>@link</literal>) which makes the
documentation far easier to use.
</para>
</section>
<section id="standards.java-formatting">
<title>Java Code Formatting</title>
<para>
Ah, a <emphasis>religous issue</emphasis>. The most important things are to be consistent
(an editor that indents code for you is helpful) and to
<emphasis>conform to the existing style</emphasis> when editting someone else's code.
</para>
<para>
Tapestry is formatted using <emphasis>spaces</emphasis> (not tabs),
and an indent of four.
</para>
<para>
All the code currently in the repository has been formatted using the
Eclipse IDE. My personal preference is to include a newline before opening braces.
In addition, a maximum line-length of 100 characters has been used. These
preferences are easy to setup in Eclipse:
</para>
<figure>
<title>Eclipse: Java Code Formatting Preferences</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/eclipse-java-formatting.png" format="PNG"/>
</imageobject>
</mediaobject>
</figure>
</section>
<section id="standards.naming">
<title>Naming Conventions</title>
<para>
Standard Java guidelines are expected to be followed.
Class names are capitalized (example: <classname>MyClass</classname>).
Methods start with a lower-case character (example: <literal>myMethod</literal>).
</para>
<para>
Static final variables used as constants
are in upper-case (example: <literal>MY_CONSTANT</literal>).
</para>
<para>
Private member variables (both instance and static) are named with a leading underscore
(example: <literal>_myVariable</literal>). Public member variables are to be avoided.
</para>
<note>
<title>Naming in transition</title>
<para>
I've resisted the leading underscore syntax for a long time; the rationale behind it
is to make it possible, at a glance, to visually seperate instance variables
from local variables and parameters. Previously, I've always maintained that
the problem was methods that were too large; lately I've changed
my mind ... the underscore naming helps when debugging and helps avoid
a number of naming collisions.
</para>
<para>
At the time of this writing, 2.1-beta-1, very little of the code used
the new naming. Over time, mixed in with other bug fixes, renaming will
occur (&Eclipse; helps with this greatly). New code will be written to conform.
</para>
</note>
<para>
Interfaces in Tapestry are prefixed with the letter 'I' (example: <literal>IRequestCycle</literal>).
Implementations (often in a different package)
strip off the 'I' (example: <literal>RequestCycle</literal>). Interfaces related to JavaBean events
do not start with an 'I' (example: <literal>PageDetachListener</literal>).
</para>
<para>
Base classes, classes which are concrete and functional, but often extended, are prefixed with 'Base' (example:
<literal>BaseComponent</literal>). Abstract classes are prefixed with 'Abstract' (example:
<literal>AbstractEngine</literal>). Classes which are functional and only rarely subclassed are often
prefixed with 'Default' (example: <literal>DefaultScriptSource</literal>).
</para>
<para>
The base package for the framework JAR (&TapestryFrameworkJar;) is <literal>org.apache.tapestry</literal>.
The base package for the contrib JAR (&TapestryContribJar;) is <literal>org.apache.tapestry.contrib</literal>.
</para>
</section>
</chapter>
<chapter id="releases">
<title>Tapestry Release Numbering</title>
<para>
Tapestry release numbering is relatively simple, as long as you don't look back in time
(the less managable numbering system used through release 2.0.5 is described shortly).
</para>
<para>
Tapestry releases consist of a major version, a minor version and a incremental version.
The pattern
<literal>
<replaceable>major</replaceable>.<replaceable>minor</replaceable>-<replaceable>incremental</replaceable>-<replaceable>index</replaceable>
</literal>
is used, for example: <literal>2.1</literal>, <literal>2.2-alpha-3</literal>
or <literal>2.3-beta-1</literal>.
</para>
<para>
The major version represents large-scale changes in the framework ... short of translating Tapestry
to another language (say, Python or Ruby), this is not likely to happen again. Tapestry
is currently in major release 2.
</para>
<para>
The minor version represents a milestone release, encompassing
the introduction of new functionality and bug fixes
in a stable manner. <literal>2.1</literal> or <literal>2.2</literal> would be examples
of milestone releases.
</para>
<para>
An incremental release represents a transition from one milestone release to the next.
Incremental releases are <literal>alpha</literal>, <literal>beta</literal> or
<literal>rc</literal> (release candidate).
Typically, after a milestone release there will
be a series of alpha, then beta, then rc releases, leading up to
the next milestone release.
A possible sequence is <literal>2.1</literal>, <literal>2.2-alpha-1</literal>,
<literal>2.2-beta-1</literal>, <literal>2.2-rc-1</literal>, <literal>2.2</literal>.
</para>
<para>
Typically, there will be several incremental releases of the same type, numbered from 1 up.
Alpha releases contain significant functionality changes, beta releases represent
bug fixes to those changes (stabilizing the changes),
and rc (release chandidate) releases are expected
to be stable versions of the next minor release (though any problems can spur further
release candidates).
</para>
<para>
Through Tapestry release 2.0.5, numbering was a bit different. Under the modern scheme,
2.0.1 would be named <literal>2.1-alpha-1</literal>, 2.0.2 would be <literal>2.1-alpha-2</literal>,
and 2.0.5 would be <literal>2.1-beta-1</literal>. Modern release numbering
begins with <literal>2.1-beta-2</literal> (the release immediately following 2.0.5).
</para>
</chapter>
<chapter id="procedures">
<title>Development Procedures</title>
<para>
This chapter defines procedures for development of Tapestry. This includes many things not directly
related to coding, such as documentation and interacting with the CVS repository.
</para>
<section id="procedures.deprecation">
<title>Deprecating methods and classes</title>
<para>
Tapestry is being used by a increasingly large community of developers and it is necessary
that they have some stability in their development.
</para>
<para>
To that end, classes and methods must follow a developer-friendly lifecycle.
If a method or class must be deleted, it should be marked as deprecated in one minor release,
and can be removed in the following minor release.
</para>
<para>
For example, a method may be marked as
deprecated in release 2.2-alpha-1. This change isn't considered
"real" until release 2.2. The method can be removed any time after that, say in release 2.3-alpha-3,
and the removal becomes "real" in release 2.3.
</para>
<para>
Don't simply mark a method as deprecated, give the end-developer
the information needed adapt their code. Use the following template
as part of the Javadoc comment:
<informalexample>
<programlisting>
@deprecated To be removed in <replaceable>Version</replaceable>.
Use {@link <replaceable>SomeClass#someMethod(...)</replaceable>} instead.
</programlisting>
</informalexample>
</para>
<para>
It is also important for the changer to make the transition as simple as possible for
the end-developer. Base classes and default implementations should be changed
to make use of the new API in such as way that, at most, a recompile
of the end-developer's classes is required.
</para>
<para>
Sometimes, changes require a lack of backwards compatibility. If
a method has to change and the old signature can't be maintained, then
simply change it ... but be sure to document the change in the
Tapestry release notes (<filename>web/new.html</filename>).
</para>
</section>
<section id="procedures.junit">
<title>JUnit Tests</title>
<para>
Tapestry has an excellent JUnit test suite, with code coverage
figures over 80% at the time of this writing (2.4-alpha-4). It is
<emphasis>required</emphasis> that changes to the framework
be accompanied by additional JUnit tests (typically, mock tests; see
below) to validate the changes. In addition, there is an ongoing
effort to fill in the gaps in the existing suite; the suite should reach
over 90% code coverage.
</para>
<para>
In order to compile and run the JUnit test suite you need to download
<literal>junit.jar</literal> and <literal>jdom-b8.jar</literal>,
and place them in the <literal>ext-dist</literal> directory.
The official sites to download the libraries are listed in the README file
in that directory.
</para>
<para>
Some of the JUnit tests now require &Jython;. You must
download and install Jython 2.1,
then configure <literal>jython.dir</literal> in <filename>config/build.properties</filename>
to point to the install directory. As usual, use an absolute path and forward slashes only. To run
the JUnit test suite within Eclipse, you must
set the <literal>JYTHON_DIR</literal> classpath variable.
</para>
<para>
JUnit test source code is placed into the <filename class="directory">junit/src</filename> source tree.
The package name for JUnit tests is <literal>org.apache.tapestry.junit</literal>.
</para>
<para>
Less than half of Tapestry is tested using traditional JUnit tests. The majority of JUnit testing occurs using
a system of mock unit tests. Mock testing involves replacing the key classes of the
Servlet API (<classname>HttpServletRequest</classname>, <classname>HttpSession</classname>, etc.) with
out own implementations, with extensions that allow for checks and validations. Instead of processing a series of
requests over HTTP, the requests are driven by an XML script file, which includes output checks.
</para>
<para>
Generally, each bit of functionality can be tested using its own mini-application.
Create the application as
<filename>junit/context<replaceable>X</replaceable></filename>. This is much easier now,
using Tapestry 3.0 features
such as dynamic lookup of specifications and implicit components.
</para>
<para>
The Mock Unit Test Suite is driven by scripts (whose structure is described below). The suite
searches the directory <filename>junit/mock-scripts</filename> for files with the ".xml" extension.
Each of these is expected to be a test script. The order in which scripts are executed is
arbitrary; scripts (and JUnit tests in general) should never rely on any order of execution.
</para>
<para>
Test scripts are named <filename>Test<replaceable>Name</replaceable>.xml</filename>.
</para>
<note>
<para>
The XML script is not validated, and invalid elements are
generally ignored. The class <classname>MockTester</classname>
performs the test, and its capabilities are in fluxx, with
new capabilities being added as needed.
</para>
</note>
<para>
A test script consists of an <sgmltag class="starttag">mock-test</sgmltag>
element. Within it, the virtual context and servlet are defined.
</para>
<programlisting>
<![CDATA[
<mock-test>
<context name="c6" root="context6"/>
<servlet name="app" class="org.apache.tapestry.ApplicationServlet">
<init-parameter name="org.apache.tapestry.engine-class"
value="org.apache.tapestry.junit.mock.c6.C6Engine"/>
</servlet>
]]>
</programlisting>
<para>
The name for the context becomes the leading term in any
generated URLs. Likewise, the servlet name becomes the second
term. The above example will generate URLs that reference
<literal>/c6/app</literal>. Specifying a <literal>root</literal>
for a context identifies the root context directory (beneath the top level
<filename>junit</filename> directory). In this example, HTML templates
go in <filename>context6</filename> and specifications
go in <filename>context6/WEB-INF</filename>.
</para>
<para>
Following the <sgmltag class="starttag">servlet</sgmltag>
and <sgmltag class="starttag">context</sgmltag> elements, a series
of <sgmltag class="starttag">request</sgmltag> elements. Each such element
simulates a request. A request specifies any query parameters passed as part
of the request, and contains a number of assertions that test
either the results, generally in terms of searching for strings
or regular expressions within the HTML response.
</para>
<programlisting>
<![CDATA[
<request>
<parameter name="service" value="direct"/>
<parameter name="context" value="0/Home/$DirectLink"/>
<assert-output name="Page Title">
<![CDATA[
<title>Persistant Page Property</title>
]]>]]&gt;<![CDATA[
</assert-output>
]]>
</programlisting>
<warning>
<para>
As in the above example, it is very important
that HTML tags be properly escaped with the XML CDATA construct.
</para>
</warning>
<para>
Adding <literal>failover="true"</literal> to the
<sgmltag class="starttag">request</sgmltag> simulates a failover. The contents
of the &HttpSession; are serialized,
then deserialized. This ensures that all the data stored into the
&HttpSession; will survive a failover to a new server within
a cluster.
</para>
<para>
All of the assertion elements expect a <literal>name</literal>
attribute, which is incorporated into any error message
if the assertion fails (that is, if the expected output
is not present).
</para>
<para>
The <sgmltag class="starttag">assert-output</sgmltag> element checks for the
presence of the contained literal output, contained within the
element. Leading and trailing whitespace is trimmed before
the check is made.
</para>
<programlisting>
<![CDATA[
<assert name="Session Attribute">
request.session.getAttribute("app/Home/message").equals("Changed")
</assert>
]]>
</programlisting>
<para>
The <sgmltag class="starttag">assert</sgmltag> element checks
that the provided OGNL expression evaluates to true.
</para>
<programlisting>
<![CDATA[
<assert-regexp name="Error Message">
<![CDATA[
<span class="error">\s*You must enter a value for Last Name\.\s*</span>
]]>]]&gt;<![CDATA[
</assert-regexp>
]]>
</programlisting>
<para>
The <sgmltag class="starttag">assert-regexp</sgmltag> looks
for a regular expression in the result, instead of a simple literal
string.
</para>
<programlisting>
<![CDATA[
<assert-output-matches name="Selected Radio" subgroup="1">
<![CDATA[
<input type="radio" name="inputSex" checked="checked" value="(.*?)"/>
]]>]]&gt;<![CDATA[
<match>1</match>
</assert-output-matches>
]]>
</programlisting>
<para>
The <sgmltag class="starttag">assert-output-matches</sgmltag>
is the most complicated assertion. It contains a regular expression
which is evaluated. For each match, the subgroup value is extracted,
and compared to the next <sgmltag class="starttag">match</sgmltag>
value. Also, the count of matches (vs. the number of
<sgmltag class="stattag">match</sgmltag> elements) is checked.
</para>
<programlisting>
<![CDATA[
<assert-output-stream name="Asset Content"
content-type="image/gif"
path="foo/bar/baz.gif"/>
]]>
</programlisting>
<para>
The <sgmltag class="starttag">assert-output-stream</sgmltag>
element is used to compare the entire response to a static file
(this is normally associated with private assets). A content type
must be specified, as well as a relative path to a file to compare
against. The path is relative to the junit directory. The
response must match the specified content type and actual content.
</para>
<programlisting>
<![CDATA[
<assert-exception name="Exception">
File foo not found.
</assert-exception>
]]>
</programlisting>
<para>
The <sgmltag class="starttag">assert-exception</sgmltag> element
is used to check when an request fails entirely (is unable to send back a
response). This only occurs when the application specification contains invalid
data (such as an incorrect class for the engine), or when the Exception page
is unable to execute. The body of the element is matched against the exception's
message property.
</para>
<note>
<title>Force a failure, then check for correctness</title>
<para>
Sometimes the tests themselves have bugs. A useful technique is
to purposely break the test to ensure that it is checking for what
it should check, then fix the test. For example, adding <literal>XXX</literal>
into a <sgmltag class="starttag">assert-output</sgmltag>. Run the test suite
and expect a failure, then remove the <literal>XXX</literal> and
re-run the test, which should succeed.
</para>
</note>
</section>
<section id="procedures.documentation">
<title>Documentation</title>
<para>
Documentation is much harder than coding, but the ongoing success of Tapestry depends on maintaining
the quality of documentation. Tapestry documentation is written using &DocBook; XML format, using
XSL stylesheets to convert to final documentation.
</para>
<para>
Changes to the framework usually require a change in documentation to the Tapestry Developer's Guide.
</para>
</section>
<section id="procedures.component-doc">
<title>Component Documentation</title>
<warning>
<para>
This section is out of date. In general, each component should
include a link to the Component Reference page for the component.
The Component Reference page has a format and content
similar to what's listed here.
</para>
</warning>
<para>
Although there is limited documentation about components in their component specification file, that documentation
is designed to be a short reminder, not the complete documentation. Full documentation goes into
the component's Java file, as part of its type comment JavaDoc.
</para>
<para>
Component documentation consists of a table, identifying all the formal parameters of the component.
In addition, a note indicating whether informal parameters are allowed, and if the component may
have a body (that is, wrap other components) is supplied at the end.
</para>
<figure>
<title>Component Documentation Template</title>
<programlisting>
/**
* <replaceable>Type comment documentation ...</replaceable>
*
* &lt;p&gt;&lt;table border=1&gt;
* &lt;tr&gt;
* &lt;th&gt;Parameter&lt;/th&gt;
* &lt;th&gt;Type&lt;/th&gt;
* &lt;th&gt;Direction&lt;/th&gt;
* &lt;th&gt;Required&lt;/th&gt;
* &lt;th&gt;Default&lt;/th&gt;
* &lt;th&gt;Description&lt;/th&gt;
* &lt;/tr&gt;
*
* &lt;tr&gt;
* &lt;td&gt;<replaceable>name</replaceable>&lt;/td&gt;
* &lt;td&gt;<replaceable>{@link Type}</replaceable>&lt;/td&gt;
* &lt;td&gt;<replaceable>in|out|in-out</replaceable>&lt;/td&gt; <co id="procedures.component-doc.direction"/>
* &lt;td&gt;<replaceable>yes|no</replaceable>&lt;/td&gt;
* &lt;td&gt;<replaceable>Default value</replaceable>&lt;/td&gt; <co id="procedures.component-doc.default"/>
* &lt;td&gt;<replaceable>Full description</replaceable>&lt;/td&gt;
* &lt;/tr&gt;
*
* ...
*
* &lt;p&gt;Informal parameters are <replaceable>[not]</replaceable> allowed. The component
* may <replaceable>[not]</replaceable> contain a body.
*
* ...
*
*/
</programlisting>
<calloutlist>
<callout arearefs="procedures.component-doc.direction">
<para>
This describes how the component uses its binding.
<literal>in</literal> indicates the binding is read, but never updated, which is
the most common case.
<literal>out</literal> indicates the binding is updated, but not read; this is rare, but
does apply to some parameters of &Foreach;, for example.
<literal>in-out</literal> is common used with certain form parameters.
</para>
</callout>
<callout arearefs="procedures.component-doc.default">
<para>
If the parameter is required, then this is usually specified as &amp;nbsp; (non-breaking space).
</para>
</callout>
</calloutlist>
</figure>
<para>
Recently, <ulink url="../ComponentReference/index.html">seperate HTML component documentation</ulink>
has been created. This will be the standard location
for Framework component documentation. Javadoc for the
component should simply have a link
to the correct Component Reference page.
</para>
<para>
The component reference is simply HTML (at least, for the time being).
There are many examples and a template available, for creating
new reference pages.
</para>
</section>
<section id="procedures.checkin">
<title>Checkin Procedures</title>
<para>
You should always follow these procedures when checking in code:
<variablelist>
<varlistentry>
<listitem>
<para>
Run JUnit tests (ant junit) before doing a checkin.
</para>
</listitem>
</varlistentry>
<varlistentry>
<listitem>
<para>
Build the Javadoc (ant javadoc) to ensure there are no errors introduced.
</para>
</listitem>
</varlistentry>
<varlistentry>
<listitem>
<para>
Add a Bugzilla bug or a feature request describing the change.
</para>
</listitem>
</varlistentry>
<varlistentry>
<listitem>
<para>
When checking code in, use the Bugzilla bug id as the checkin comment.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<example>
<title>Example checkin comment</title>
<programlisting>
[ 553310 ] Set properties from parameter bindings
</programlisting>
</example>
<para>
In addition, update the Tapestry release notes, the file <filename>web/new.html</filename>,
to identify the feature request.
</para>
<para>
If you are adding new code, please make sure that the code contains:
<variablelist>
<varlistentry>
<listitem>
<para>
The Apache license in a comment block at the beginning.
</para>
</listitem>
</varlistentry>
<varlistentry>
<listitem>
<para>
The &dollar;Id$ symbol as described above.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Be very careful when checking files in that they are checked in with the correct
keyword substitution type.
Files should be either binary or text; text should be checked in with keyword
expansion turned on (this is the <literal>-kkv</literal> option).
</para>
<para>
When new files are added using Eclipse, it must decide whether they are
binary or text. Eclipse always assumes <emphasis>binary</emphasis>
unless specifically informed that a file is text. Use the Team preferences
panel to set this.
</para>
<figure>
<title>Eclipse: Team Preferences</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/eclipse-team-preferences.png" format="PNG"/>
</imageobject>
</mediaobject>
</figure>
<para>
Finally, if major changes are enacted,
it is good to ensure that the framework continues to be compatible
with the API versions declared in the User Guide. This can be verified
by performing the following actions:
<variablelist>
<varlistentry>
<listitem>
<para>
Compile the framework using Java 1.2.2.
(e.g. by setting <literal>JAVA_HOME</literal> and running <literal>ant</literal>)
</para>
</listitem>
</varlistentry>
<varlistentry>
<listitem>
<para>
Run the unit tests using both Java 1.3.x and 1.4.x
(e.g. by setting <literal>JAVA_HOME</literal> and running 'ant junit').
Running the unit tests under 1.3.x would require adding
the <literal>Xerces</literal> libraries to the classpath
(e.g. to <filename>lib/ext</filename>).
</para>
</listitem>
</varlistentry>
<varlistentry>
<listitem>
<para>
Compile with Java Servlet API 2.3 and run the unit tests
using Java Servlet API 2.2
(e.g. by compiling, then pointing the <literal>servlet.jar</literal> setting in
the <filename>config/build.properties</filename> file to the
Java Servlet API 2.2 library and running 'ant junit')
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</section>
<section id="procedures.examples">
<title>Creating Examples</title>
<para>
Extending the Workbench application to demonstrate new features or components
is expected for any significant changes or additions to the framework, or
to the contrib library.
</para>
</section>
<section id="procedures.copyrights">
<title>Updating Copyrights</title>
<para>
All source code stored in the repository must contain the standard Apache copyright and license. A copy of the
license, as a comment block, is stored as <filename>support/license.txt</filename>
</para>
<para>
Realize that you are assigning copyright to the Apache Software Foundation.
</para>
<para>
The contents of this file can be pasted in directly before the <literal>package</literal> statement of a Java source file.
</para>
<para>
Alternately, a <ulink url="http://www.python.org">Python</ulink> script is provided which
can locate all Java source files within a directory tree and ensure that the leading comment block
is correct. It modifies any source files where the leading comment doesn't match, but does not modify
any files where the leading comment matches.
</para>
<para>
To use the script, execute the command
<command>python support/update-copyrights.py LICENSE.txt <replaceable>directory ...</replaceable></command>.
</para>
<para>
You may specify any number of directories, though the script is fast enough that just using "." (for current directory)
is easiest.
</para>
<warning>
<title>Cygwin Python</title>
<para>
On my computer (running Windows XP and/or 2000), when using the
<ulink url="http://sources.redhat.com/cygwin">Cygwin</ulink>
version of Python, it is necessary to execute the script
from the Bash shell, not the standard Windows command line.
</para>
</warning>
</section> <!-- procedures.copyrights -->
</chapter>
</book>