This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tapestry-5-site.git

commit 1619244f887d6dbb33bd0c2a8631477336dc40dd
Author: Volker Lamp <[email protected]>
AuthorDate: Wed Dec 18 23:02:12 2024 +0100

    Added Tutorial. Moved Getting Started from User Guide to main page.
---
 antora.yml                                         |   3 +-
 modules/ROOT/images/newapp_Index.png               | Bin 0 -> 155165 bytes
 modules/ROOT/nav.adoc                              |  13 +-
 modules/ROOT/pages/getting-started.adoc            | 231 ++++++++++
 .../pages/supported-environments-and-versions.adoc | 257 +++++++++++
 modules/ROOT/pages/userguide.adoc                  |   3 +
 modules/tutorial/images/add-archetype-catalog.png  | Bin 0 -> 121925 bytes
 modules/tutorial/images/address-v3.png             | Bin 0 -> 26641 bytes
 modules/tutorial/images/address-v5.png             | Bin 0 -> 30842 bytes
 modules/tutorial/images/address-v6.png             | Bin 0 -> 38225 bytes
 modules/tutorial/images/address-v7.png             | Bin 0 -> 22925 bytes
 modules/tutorial/images/address-v8.png             | Bin 0 -> 21592 bytes
 modules/tutorial/images/app-error-1.png            | Bin 0 -> 118491 bytes
 modules/tutorial/images/app-error-2.png            | Bin 0 -> 155279 bytes
 modules/tutorial/images/app-live-reload.png        | Bin 0 -> 95958 bytes
 modules/tutorial/images/console-startup.png        | Bin 0 -> 56249 bytes
 modules/tutorial/images/create-address-initial.png | Bin 0 -> 24144 bytes
 .../tutorial/images/create-address-reordered.png   | Bin 0 -> 24577 bytes
 modules/tutorial/images/gameover.png               | Bin 0 -> 6290 bytes
 modules/tutorial/images/guess-1.png                | Bin 0 -> 13282 bytes
 modules/tutorial/images/guess-no-target-prop.png   | Bin 0 -> 55396 bytes
 modules/tutorial/images/guess-target-zero.png      | Bin 0 -> 5508 bytes
 modules/tutorial/images/guess-target.png           | Bin 0 -> 5534 bytes
 modules/tutorial/images/guess-template-missing.png | Bin 0 -> 52865 bytes
 modules/tutorial/images/hilo-1.png                 | Bin 0 -> 7951 bytes
 .../images/hilo-index-missing-action-error.png     | Bin 0 -> 71644 bytes
 modules/tutorial/images/hmac-warning.png           | Bin 0 -> 14513 bytes
 modules/tutorial/images/index-grid-v1.png          | Bin 0 -> 38846 bytes
 modules/tutorial/images/run-configuration-jre.png  | Bin 0 -> 66005 bytes
 modules/tutorial/images/run-configuration.png      | Bin 0 -> 81765 bytes
 modules/tutorial/images/select-a-wizard.png        | Bin 0 -> 45920 bytes
 modules/tutorial/images/select-archetype.png       | Bin 0 -> 56858 bytes
 .../images/specify-archetype-parameters.png        | Bin 0 -> 48923 bytes
 modules/tutorial/images/startpage.png              | Bin 0 -> 73313 bytes
 .../tutorial/images/templates_and_parameters.png   | Bin 0 -> 89564 bytes
 modules/tutorial/nav.adoc                          |   8 +
 .../pages/creating-the-skeleton-application.adoc   | 115 +++++
 .../pages/dependencies-tools-and-plugins.adoc      |  35 ++
 modules/tutorial/pages/exploring-the-project.adoc  | 449 ++++++++++++++++++
 .../implementing-the-hi-lo-guessing-game.adoc      | 510 +++++++++++++++++++++
 modules/tutorial/pages/index.adoc                  |  57 +++
 .../using-beaneditform-to-create-user-forms.adoc   | 415 +++++++++++++++++
 .../pages/using-tapestry-with-hibernate.adoc       | 262 +++++++++++
 modules/tutorial/partials/diagrams/hilo-flow.puml  |  10 +
 44 files changed, 2366 insertions(+), 2 deletions(-)

diff --git a/antora.yml b/antora.yml
index 0d09fed..250aa7f 100644
--- a/antora.yml
+++ b/antora.yml
@@ -1,9 +1,10 @@
 name: main
-title: Tapestry
+title: Home
 version: ~
 start_page: ROOT:about.adoc
 nav:
 - modules/ROOT/nav.adoc
 asciidoc:
   attributes:
+    experimental: true # must be true to support User Interface Macros
     tapestry-version: 5.8.7
diff --git a/modules/ROOT/images/newapp_Index.png 
b/modules/ROOT/images/newapp_Index.png
new file mode 100644
index 0000000..6091a61
Binary files /dev/null and b/modules/ROOT/images/newapp_Index.png differ
diff --git a/modules/ROOT/nav.adoc b/modules/ROOT/nav.adoc
index 72102f1..90ae27d 100644
--- a/modules/ROOT/nav.adoc
+++ b/modules/ROOT/nav.adoc
@@ -1,5 +1,16 @@
 * xref:about.adoc[]
 * xref:download.adoc[]
-* xref:documentation.adoc[]
+* Documentation
+** xref:getting-started.adoc[]
+** Tutorial
+*** xref:tutorial:index.adoc[]
+*** xref:tutorial:dependencies-tools-and-plugins.adoc[]
+*** xref:tutorial:creating-the-skeleton-application.adoc[]
+*** xref:tutorial:exploring-the-project.adoc[]
+*** xref:tutorial:implementing-the-hi-lo-guessing-game.adoc[]
+*** xref:tutorial:using-beaneditform-to-create-user-forms.adoc[]
+*** xref:tutorial:using-tapestry-with-hibernate.adoc[]
+** xref:userguide.adoc[]
 * xref:community.adoc[]
 * xref:developers.adoc[]
+
diff --git a/modules/ROOT/pages/getting-started.adoc 
b/modules/ROOT/pages/getting-started.adoc
new file mode 100644
index 0000000..80faf46
--- /dev/null
+++ b/modules/ROOT/pages/getting-started.adoc
@@ -0,0 +1,231 @@
+= Getting Started
+
+Getting started with Tapestry is easy, and you have lots of ways to begin: 
watch a video, browse the source code of a working demo app, create a skeleton 
app using Maven, or step through the tutorial.
+
+== Watch a short video
+For a fast-paced introduction, watch Mark W. Shead's 
http://blog.markshead.com/900/tapestry-5-10-minute-demo/[10 Minute Demo].
+This video shows how to set up a simple Tapestry application, complete with 
form validation, Hibernate-based persistence, and Ajax.
+The video provides a preview of the development speed and productivity that 
experienced Tapestry users enjoy.
+
+== Play with a working demo app
+You can also play with Tapestry via our live demonstration applications.
+To start, have a look at the https://tapestry-app.apache.org/hotels/[Hotel 
Booking Demo].
+The http://github.com/bobharner/tapestry5-hotel-booking-5.4/[source code] is 
provided so you can download and play with it.
+Also check out the https://tapestry-jumpstart.org/jumpstart/[Jumpstart 
demonstration site].
+
+== Create your first Tapestry project
+The easiest way to start a new app is to use https://maven.apache.org/[Apache 
Maven] to create your initial project;
+Maven can use an archetype (a kind of project template) to create a bare-bones 
Tapestry application for you.
+
+Once you have Maven installed, execute the following command:
+
+----
+mvn archetype:generate -Dfilter=org.apache.tapestry:quickstart
+----
+
+(Alternatively, if you want to get an archetype for a not-yet-released version 
of Tapestry – most users don't – you can use the staging URI, 
https://repository.apache.org/content/repositories/staging ).
+// TODO explain how to use the stagingn URI 
+
+Maven will prompt you for the archetype to create ("Tapestry 5 Quickstart 
Project") and the exact version number (e.g., "5.8.7").
+It also asks you for a group id, an artifact id, and a version number. You can 
see this in the following transcript:
+
+----
+$ mvn archetype:generate -DarchetypeCatalog=http://tapestry.apache.org
+[INFO] Scanning for projects...
+[INFO]                                                                         
+[INFO] ------------------------------------------------------------------------
+[INFO] Building Maven Stub Project (No POM) 1
+[INFO] ------------------------------------------------------------------------
+[INFO] 
+[INFO] >>> maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom 
>>>
+[INFO] 
+[INFO] <<< maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom 
<<<
+[INFO] 
+[INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom 
---
+[INFO] Generating project in Interactive mode
+[INFO] No archetype defined. Using maven-archetype-quickstart 
(org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
+Choose archetype:
+1: http://tapestry.apache.org -> org.apache.tapestry:quickstart (Tapestry 5 
Quickstart Project)
+2: http://tapestry.apache.org -> org.apache.tapestry:tapestry-archetype 
(Tapestry 5.4.5 Archetype)
+Choose a number or apply filter (format: [groupId:]artifactId, case sensitive 
contains): : 1
+Choose org.apache.tapestry:quickstart version: 
+1: 5.0.19
+2: 5.1.0.5
+3: 5.2.6
+4: 5.3.7
+5: 5.4.5
+6: 5.5.0
+7: 5.6.4
+8: 5.7.3
+9: 5.8.7
+Choose a number: 9: 9
+Define value for property 'groupId': : com.example
+Define value for property 'artifactId': : newapp
+Define value for property 'version':  1.0-SNAPSHOT: : 
+Define value for property 'package':  com.example: : com.example.newapp
+Confirm properties configuration:
+groupId: com.example
+artifactId: newapp
+version: 1.0-SNAPSHOT
+package: com.example.newapp
+ Y: : Y
+[INFO] 
----------------------------------------------------------------------------
+[INFO] Using following parameters for creating project from Archetype: 
quickstart:5.8.6
+[INFO] 
----------------------------------------------------------------------------
+[INFO] Parameter: groupId, Value: com.example
+[INFO] Parameter: artifactId, Value: newapp
+[INFO] Parameter: version, Value: 1.0-SNAPSHOT
+[INFO] Parameter: package, Value: com.example.newapp
+[INFO] Parameter: packageInPathFormat, Value: com/example/newapp
+[INFO] Parameter: package, Value: com.example.newapp
+[INFO] Parameter: version, Value: 1.0-SNAPSHOT
+[INFO] Parameter: groupId, Value: com.example
+[INFO] Parameter: artifactId, Value: newapp
+
+[INFO] project created from Archetype in dir: /home/joeuser/junk/junk2/newapp
+[INFO] ------------------------------------------------------------------------
+[INFO] BUILD SUCCESS
+[INFO] ------------------------------------------------------------------------
+[INFO] Total time: 40.020s
+[INFO] Finished at: Sun Apr 09 16:55:01 EDT 2020
+[INFO] Final Memory: 16M/303M
+[INFO] ------------------------------------------------------------------------
+----
+
+Maven will (after performing a number of one-time downloads) create a skeleton 
project ready to run.
+Because we specified an artifactId of "newapp", the project is created in the 
`newapp` directory.
+(Note: if you get "Unable to get resource" warnings at this stage, you may be 
behind a firewall which blocks outbound HTTP requests to Maven repositories.)
+
+To run the skeleton application, change to the `newapp` directory and execute 
the `mvn jetty:run` command to start the Jetty app server:
+
+----
+$ cd newapp
+$ mvn jetty:run
+[INFO] Scanning for projects...
+[INFO]
+[INFO] ------------------------------------------------------------------------
+[INFO] Building newapp Tapestry 5 Application 1.0-SNAPSHOT
+[INFO] ------------------------------------------------------------------------
+
+...
+
+Application 'app' (version 1.0-SNAPSHOT-DEV) startup time: 329 ms to build IoC 
Registry, 919 ms overall.
+ ______                  __             ____
+/_  __/__ ____  ___ ___ / /_______ __  / __/
+ / / / _ `/ _ \/ -_|_-</ __/ __/ // / /__ \
+/_/  \_,_/ .__/\__/___/\__/_/  \_, / /____/
+        /_/                   /___/  5.8.7 (development mode)
+
+[INFO] Started [email protected]:8080
+[INFO] Started Jetty Server
+----
+
+After some more one-time downloads you can open your browser to 
`http://localhost:8080/newapp` to see the application running:
+
+image::newapp_Index.png[Newapp Index page]
+
+The application consists of three pages sharing a common look and feel.
+The initial page, `Index`, allows you to perform some basic operations.
+
+You can also load the newly-created project it into any IDE and start coding.
+See the next section on where to find the different components of the 
application.
+
+== Exploring the generated project
+The archetype creates the following files:
+
+----
+newapp/
+├── build.gradle
+├── gradle
+│   └── wrapper
+│       ├── gradle-wrapper.jar
+│       └── gradle-wrapper.properties
+├── gradlew
+├── gradlew.bat
+├── pom.xml
+└── src
+    ├── main
+    │   ├── java
+    │   │   └── com
+    │   │       └── example
+    │   │           └── newapp
+    │   │               ├── components
+    │   │               │   └── Layout.java
+    │   │               ├── pages
+    │   │               │   ├── About.java
+    │   │               │   ├── Contact.java
+    │   │               │   ├── Error404.java
+    │   │               │   ├── Index.java
+    │   │               │   └── Login.java
+    │   │               └── services
+    │   │                   ├── AppModule.java
+    │   │                   ├── DevelopmentModule.java
+    │   │                   └── QaModule.java
+    │   ├── resources
+    │   │   ├── com
+    │   │   │   └── example
+    │   │   │       └── newapp
+    │   │   │           ├── components
+    │   │   │           │   └── Layout.tml
+    │   │   │           ├── logback.xml
+    │   │   │           └── pages
+    │   │   │               ├── About.tml
+    │   │   │               ├── Contact.tml
+    │   │   │               ├── Error404.tml
+    │   │   │               ├── Index.properties
+    │   │   │               ├── Index.tml
+    │   │   │               └── Login.tml
+    │   │   └── log4j.properties
+    │   └── webapp
+    │       ├── WEB-INF
+    │       │   ├── app.properties
+    │       │   └── web.xml
+    │       ├── favicon.ico
+    │       ├── images
+    │       │   └── tapestry.png
+    │       └── mybootstrap
+    │           ├── css
+    │           │   ├── bootstrap-responsive.css
+    │           │   └── bootstrap.css
+    │           ├── img
+    │           │   ├── glyphicons-halflings-white.png
+    │           │   └── glyphicons-halflings.png
+    │           └── js
+    │               └── bootstrap.js
+    ├── site
+    │   ├── apt
+    │   │   └── index.apt
+    │   └── site.xml
+    └── test
+        ├── conf
+        │   ├── testng.xml
+        │   └── webdefault.xml
+        ├── java
+        │   └── PLACEHOLDER
+        └── resources
+            └── PLACEHOLDER
+30 directories, 39 files
+----
+
+A Tapestry application is composed of pages, each page consisting of one 
template file and one Java class.
+
+Tapestry page templates have the `.tml` extension and are found within 
`src/main/*resources*/` under the app's `pages` package 
(`src/main/resources/com/example/newapp/*pages*`, in this example).
+Templates are essentially HTML with some special markup to reference 
properties in the corresponding Java class and to reference ready-made or 
custom components.
+
+Similarly, Tapestry page classes are found in within the `src/main/*java*` 
under the app's `pages` package (`src/main/java/com/example/newapp/*pages*`, in 
this example) and their name matches their template name (`Index.tml` -> 
`Index.java`).
+
+In the skeleton project, most of the HTML is not found on the pages themselves 
but in a Layout component which acts as a global template for the whole site.
+Java classes for components live in 
`src/main/*java*/com/example/newapp/*components*` and component templates go in 
`src/main/*resources*/com/example/newapp/*components*`.
+
+The archetype includes a few optional extras:
+
+* The bundled version of the Bootstrap CSS library has a per-project override.
+You can see the files in `src/webapp/context/mybootstrap`, and the overrides 
to enable that in `AppModule.java`.
+* By default, Tapestry uses http://prototypejs.org/[Prototype] as its 
client-side library, but the archetype overrides this to 
https://jquery.org/[jQuery], which is preferred for new projects.
+* The archetype adds a simple filter that shows the timing of each request.
+* The archetype sets up not just for builds with Maven, but also via 
http://gradle.org/[Gradle].
+
+== What's next?
+To deepen your understanding, step through the 
xref:tutorial:index.adoc[Tapestry Tutorial], which goes into much more detail 
about setting up your project as well as loading it into Eclipse... then 
continues on to teach you more about Tapestry.
+
+Be sure to read about the core xref:principles.adoc[Tapestry Principles], and 
browse the xref:userguide::index.adoc[User Guide].
diff --git a/modules/ROOT/pages/supported-environments-and-versions.adoc 
b/modules/ROOT/pages/supported-environments-and-versions.adoc
new file mode 100644
index 0000000..b34b6f4
--- /dev/null
+++ b/modules/ROOT/pages/supported-environments-and-versions.adoc
@@ -0,0 +1,257 @@
+= Supported Environments and Versions
+
+Tapestry is compatible with a wide range of app servers, Java versions, and 
open source libraries. Not all combinations are known to work, however.
+
+Note: blanks in the support matrix tables below do NOT indicate 
incompatibility. They are just documentation gaps.
+
+If you know of any other known compatibilities or incompatibilities, please 
add a comment on the 
http://mail-archives.apache.org/mod_mbox/tapestry-users/[Tapestry Users mailing 
list].
+
+== Java & Servlet API Version
+
+[%autowidth]
+|===
+|Tapestry | 5.8.4+ | 5.8.1 - 5.8-3 |5.7 |5.6 |5.5 |5.4 |5.3.8 |5.3.0 - 5.3.7 
|5.0 - 5.2
+
+|Java JRE
+|8-21
+|8-17
+|8-14
+|8-14
+|8-12
+|7,8
+|6-8 footnote:[For using Tapestry 5.3.8 with Java 8 see 
xref:userguide::release-notes-5.3.adoc#_release_notes_5_3_8[Release Notes 
5.3.8].]
+|6,7
+|5,6
+
+|Servlet API
+|3.0+ 
+|3.0+
+|3.0+
+|3.0+
+|3.0+
+|2.5+
+|2.5+
+|2.5+
+|2.4+
+|===
+
+== App Servers
+[%autowidth]
+|===
+|Tapestry |5.5+ |5.4 |5.3.8 |5.3.0 - 5.3.7 |5.2 |5.1 | 5.0.10 |5.0.8|5.0
+
+|Apache Tomcat
+|6+
+|
+|
+|6+
+|
+|
+|
+|
+|
+
+|Jetty
+|9
+|7-9
+|6-9
+|6-8
+|6-8
+|6-8
+|6-8
+|6-8
+|6-8
+
+|Glassfish
+|
+|
+|
+|
+|
+|2.1
+|
+|
+|
+
+|JBoss EAP
+|
+|4.2.3
+|
+|
+|
+|
+|
+|
+|4+
+|===
+
+== Libraries
+
+These are the library versions known to work (and, in some cases, bundled with 
Tapestry). Unless otherwise noted, adjacent versions will often work fine as 
well.
+
+[%autowidth]
+|===
+|Tapestry |5.7.3+ |5.5+ |5.4.1 |5.4 |5.3.8 |5.3.7 | 5.3.3-5.3.6 |5.3.2|5.3 - 
5.3.1 |5.2.1 - 5.2.0 |5.1 |5.0.16 |5.0.10 |5.0.8 |5.0
+
+|Hibernate
+|5.4.32.Final
+|5.1.0.Final
+|
+|? - 4.3.6
+|
+|
+|
+|
+|
+|3.5.4 - 3.6.0
+|
+|3.3.0+
+|
+|
+|
+
+|Spring
+|
+|
+|
+|
+|
+|
+|
+|3.1.0
+|
+|
+|
+|
+|
+|
+|
+
+|jQuery.js
+|
+|
+|1.12.1
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+
+|Prototype.js
+|
+|
+|
+|1.7.1
+|
+|1.7
+|1.7
+|1.7
+|1.7
+|1.6.1
+|1.6.0.3
+|1.6.0.3
+|1.6.0.2
+|1.6
+|1.6
+
+|Scriptaculous
+|
+|
+|
+|
+|1.9
+|1.9
+|1.9
+|1.9
+|1.9
+|1.8.2
+|1.8.2
+|
+|
+|
+|1.8.0
+
+|Bootstrap CSS
+|
+|4.3.1, 3.3.6
+|3.3.6
+|3.0.2
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+
+|Moment.js
+|
+|
+|2.12.0
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+
+|Less4J
+|
+|
+|
+|1.2.1 - 1.9
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+|
+
+|Underscore.js
+|
+|
+|
+|
+|
+|
+|1.3.3
+|1.1.7
+|1.1.7
+|
+|
+|
+|
+|
+|
+|===
+
+[IMPORTANT]
+====
+*Java 9+ Dependency Deprecations.*
+With the introduction of the http://openjdk.java.net/projects/jigsaw/[Java 
module system] in version 9, various Java EE dependencies were declared 
deprecated, and removed entirely in version 11.
+This might lead to `java.lang.NoClassDefFoundError` exceptions for  
`javax`-package classes, like missing `javax.xml.bind.JAXBException` for 
`tapstry-hibernate`.
+Until all related libraries and frameworks add the now missing dependencies 
explicitly, you might have to re-add them yourself, if no other dependency is 
pulling them into your project.
+See 
https://stackoverflow.com/questions/43574426/how-to-resolve-java-lang-noclassdeffounderror-javax-xml-bind-jaxbexception
 and https://crunchify.com/java-11-and-javax-xml-bind-jaxbcontext/ for more 
information about the deprecation and removal of the Java EE dependencies, and 
how to remedy.
+====
diff --git a/modules/ROOT/pages/userguide.adoc 
b/modules/ROOT/pages/userguide.adoc
new file mode 100644
index 0000000..d31daf8
--- /dev/null
+++ b/modules/ROOT/pages/userguide.adoc
@@ -0,0 +1,3 @@
+= User Guide
+
+The versioned User Guide is located in a dedicated section of this website 
available through the navigation or this  xref:userguide::index.adoc[direct 
link to the User Guide].
diff --git a/modules/tutorial/images/add-archetype-catalog.png 
b/modules/tutorial/images/add-archetype-catalog.png
new file mode 100644
index 0000000..33af39c
Binary files /dev/null and b/modules/tutorial/images/add-archetype-catalog.png 
differ
diff --git a/modules/tutorial/images/address-v3.png 
b/modules/tutorial/images/address-v3.png
new file mode 100644
index 0000000..eddff6d
Binary files /dev/null and b/modules/tutorial/images/address-v3.png differ
diff --git a/modules/tutorial/images/address-v5.png 
b/modules/tutorial/images/address-v5.png
new file mode 100644
index 0000000..b604da1
Binary files /dev/null and b/modules/tutorial/images/address-v5.png differ
diff --git a/modules/tutorial/images/address-v6.png 
b/modules/tutorial/images/address-v6.png
new file mode 100644
index 0000000..ebfe511
Binary files /dev/null and b/modules/tutorial/images/address-v6.png differ
diff --git a/modules/tutorial/images/address-v7.png 
b/modules/tutorial/images/address-v7.png
new file mode 100644
index 0000000..a43a73c
Binary files /dev/null and b/modules/tutorial/images/address-v7.png differ
diff --git a/modules/tutorial/images/address-v8.png 
b/modules/tutorial/images/address-v8.png
new file mode 100644
index 0000000..47afaa4
Binary files /dev/null and b/modules/tutorial/images/address-v8.png differ
diff --git a/modules/tutorial/images/app-error-1.png 
b/modules/tutorial/images/app-error-1.png
new file mode 100644
index 0000000..d305aad
Binary files /dev/null and b/modules/tutorial/images/app-error-1.png differ
diff --git a/modules/tutorial/images/app-error-2.png 
b/modules/tutorial/images/app-error-2.png
new file mode 100644
index 0000000..e228db6
Binary files /dev/null and b/modules/tutorial/images/app-error-2.png differ
diff --git a/modules/tutorial/images/app-live-reload.png 
b/modules/tutorial/images/app-live-reload.png
new file mode 100644
index 0000000..9708a04
Binary files /dev/null and b/modules/tutorial/images/app-live-reload.png differ
diff --git a/modules/tutorial/images/console-startup.png 
b/modules/tutorial/images/console-startup.png
new file mode 100644
index 0000000..45e6133
Binary files /dev/null and b/modules/tutorial/images/console-startup.png differ
diff --git a/modules/tutorial/images/create-address-initial.png 
b/modules/tutorial/images/create-address-initial.png
new file mode 100644
index 0000000..da84158
Binary files /dev/null and b/modules/tutorial/images/create-address-initial.png 
differ
diff --git a/modules/tutorial/images/create-address-reordered.png 
b/modules/tutorial/images/create-address-reordered.png
new file mode 100644
index 0000000..de0f3c8
Binary files /dev/null and 
b/modules/tutorial/images/create-address-reordered.png differ
diff --git a/modules/tutorial/images/gameover.png 
b/modules/tutorial/images/gameover.png
new file mode 100644
index 0000000..1b6dd4d
Binary files /dev/null and b/modules/tutorial/images/gameover.png differ
diff --git a/modules/tutorial/images/guess-1.png 
b/modules/tutorial/images/guess-1.png
new file mode 100644
index 0000000..cb3bfc8
Binary files /dev/null and b/modules/tutorial/images/guess-1.png differ
diff --git a/modules/tutorial/images/guess-no-target-prop.png 
b/modules/tutorial/images/guess-no-target-prop.png
new file mode 100644
index 0000000..ab555bf
Binary files /dev/null and b/modules/tutorial/images/guess-no-target-prop.png 
differ
diff --git a/modules/tutorial/images/guess-target-zero.png 
b/modules/tutorial/images/guess-target-zero.png
new file mode 100644
index 0000000..cfd5b70
Binary files /dev/null and b/modules/tutorial/images/guess-target-zero.png 
differ
diff --git a/modules/tutorial/images/guess-target.png 
b/modules/tutorial/images/guess-target.png
new file mode 100644
index 0000000..ef5c2cc
Binary files /dev/null and b/modules/tutorial/images/guess-target.png differ
diff --git a/modules/tutorial/images/guess-template-missing.png 
b/modules/tutorial/images/guess-template-missing.png
new file mode 100644
index 0000000..7e7bfea
Binary files /dev/null and b/modules/tutorial/images/guess-template-missing.png 
differ
diff --git a/modules/tutorial/images/hilo-1.png 
b/modules/tutorial/images/hilo-1.png
new file mode 100644
index 0000000..073cf81
Binary files /dev/null and b/modules/tutorial/images/hilo-1.png differ
diff --git a/modules/tutorial/images/hilo-index-missing-action-error.png 
b/modules/tutorial/images/hilo-index-missing-action-error.png
new file mode 100644
index 0000000..cf09164
Binary files /dev/null and 
b/modules/tutorial/images/hilo-index-missing-action-error.png differ
diff --git a/modules/tutorial/images/hmac-warning.png 
b/modules/tutorial/images/hmac-warning.png
new file mode 100644
index 0000000..458a512
Binary files /dev/null and b/modules/tutorial/images/hmac-warning.png differ
diff --git a/modules/tutorial/images/index-grid-v1.png 
b/modules/tutorial/images/index-grid-v1.png
new file mode 100644
index 0000000..97a0c7f
Binary files /dev/null and b/modules/tutorial/images/index-grid-v1.png differ
diff --git a/modules/tutorial/images/run-configuration-jre.png 
b/modules/tutorial/images/run-configuration-jre.png
new file mode 100644
index 0000000..97e314c
Binary files /dev/null and b/modules/tutorial/images/run-configuration-jre.png 
differ
diff --git a/modules/tutorial/images/run-configuration.png 
b/modules/tutorial/images/run-configuration.png
new file mode 100644
index 0000000..2f2c899
Binary files /dev/null and b/modules/tutorial/images/run-configuration.png 
differ
diff --git a/modules/tutorial/images/select-a-wizard.png 
b/modules/tutorial/images/select-a-wizard.png
new file mode 100644
index 0000000..079bd81
Binary files /dev/null and b/modules/tutorial/images/select-a-wizard.png differ
diff --git a/modules/tutorial/images/select-archetype.png 
b/modules/tutorial/images/select-archetype.png
new file mode 100644
index 0000000..749fd72
Binary files /dev/null and b/modules/tutorial/images/select-archetype.png differ
diff --git a/modules/tutorial/images/specify-archetype-parameters.png 
b/modules/tutorial/images/specify-archetype-parameters.png
new file mode 100644
index 0000000..92d9f78
Binary files /dev/null and 
b/modules/tutorial/images/specify-archetype-parameters.png differ
diff --git a/modules/tutorial/images/startpage.png 
b/modules/tutorial/images/startpage.png
new file mode 100644
index 0000000..49164b6
Binary files /dev/null and b/modules/tutorial/images/startpage.png differ
diff --git a/modules/tutorial/images/templates_and_parameters.png 
b/modules/tutorial/images/templates_and_parameters.png
new file mode 100644
index 0000000..8fc6edf
Binary files /dev/null and 
b/modules/tutorial/images/templates_and_parameters.png differ
diff --git a/modules/tutorial/nav.adoc b/modules/tutorial/nav.adoc
new file mode 100644
index 0000000..895fa66
--- /dev/null
+++ b/modules/tutorial/nav.adoc
@@ -0,0 +1,8 @@
+* xref:index.adoc[]
+* xref:dependencies-tools-and-plugins.adoc[]
+* xref:creating-the-skeleton-application.adoc[]
+* xref:exploring-the-project.adoc[]
+* xref:implementing-the-hi-lo-guessing-game.adoc[]
+* xref:using-beaneditform-to-create-user-forms.adoc[]
+* xref:using-tapestry-with-hibernate.adoc[]
+
diff --git a/modules/tutorial/pages/creating-the-skeleton-application.adoc 
b/modules/tutorial/pages/creating-the-skeleton-application.adoc
new file mode 100644
index 0000000..3d902c0
--- /dev/null
+++ b/modules/tutorial/pages/creating-the-skeleton-application.adoc
@@ -0,0 +1,115 @@
+= Creating the Skeleton Application
+
+First, let's create an empty application. Tapestry provides a Maven 
*archetype* (a project template) to make this easy.
+
+For the tutorial, we're using a fresh install of Eclipse and an empty 
workspace at `/users/joeuser/workspace`.
+You may need to adjust a few things for other operating systems or local paths.
+
+== Using the Quickstart Archetype
+From Eclipse, we'll use a Maven archetype to create a skeleton Tapestry 
project.
+
+=== Maven Behind a Firewall
+If you are behind a firewall/proxy, before performing any Maven downloads, you 
may need to configure your proxy settings in your Maven `settings.xml` file 
(typically in the `.m2` subdirectory of your home directory, `~/.m2` or 
`C:\users\joeuser\.m2`).
+Here is an example (but check with your network administrator for the names 
and numbers you should use here).
+
+.setting.xml
+[source,xml]
+----
+<settings>
+  <proxies>
+    <proxy>
+      <active>true</active>
+      <protocol>http</protocol>
+      <host>myProxyServer.com</host>
+      <port>3128</port>
+      <username>joeuser</username>
+      <password>myPassword</password>
+      <nonProxyHosts></nonProxyHosts>
+    </proxy>
+  </proxies>
+  <localRepository>C:/Users/joeuser/.m2/repository</localRepository> <1>
+</settings>
+----
+<1> Of course, adjust the localRepository element to match the correct path 
for your computer.
+
+=== Create Project
+Okay, let's get started creating our new project.
+
+[NOTE]
+====
+The instructions below use Eclipse's New Project wizard to create the project 
from a Maven archetype.
+If you'd rather use the `mvn` command line, see the 
xref:ROOT:getting-started.adoc#_create_your_first_tapestry_project[Getting 
Started] instructions, then skip to 
xref:creating-the-skeleton-application.adoc[] page.
+====
+
+In Eclipse, go to menu:File[New > Project... > Maven > Maven Project]
+
+image::select-a-wizard.png[]
+
+Then click btn:[Next], btn:[Next] (again), and then on the "Select an 
Archetype" page click the btn:[Configure] button on the "Catalog" line.
+The "Archetype" preferences dialog should appear.
+Click the btn:[Add Remote Catalog...] button, as shown below:
+
+image::add-archetype-catalog.png[]
+
+As shown above, enter `http://tapestry.apache.org` in the "Catalog File" 
field, and "Apache Tapestry" in the Description field.
+
+Click btn:[OK], then btn:[OK] again.
+
+On the "Select an Archetype" dialog (shown below), select the newly-added 
Apache Tapestry catalog, then select the "quickstart" artifact from the list 
and click btn:[Next].
+
+image::select-archetype.png[]
+
+NOTE: Screenshots in this tutorial may show different (either newer or older) 
versions of Tapestry than you may see.
+
+Fill in the Group Id, Artifact Id, Version and Package  as follows:
+
+image::specify-archetype-parameters.png[]
+
+then click btn:[Finish].
+
+[NOTE]
+====
+The first time you use Maven, project creation may take a while as Maven 
downloads a large number of JAR dependencies for Maven, Jetty and Tapestry.
+These downloaded files are cached locally and will not need to be downloaded 
again, but you do have to be patient on first use.
+====
+
+After Maven finishes, you'll see a new directory, `tutorial1`, in your Package 
Explorer view in Eclipse.
+
+=== Running the Application using Jetty
+One of the first things you can do is use Maven to run Jetty directly.
+
+Right-click on the `tutorial1` project in your Package Explorer view and 
select menu:Run As[Maven Build... >], enter a Goal of `jetty:run`.
+This creates a "Run Configuration" named "tutorial1" that we'll use throughout 
this tutorial to start the app:
+
+image::run-configuration.png[]
+
+Tapestry runs best with a couple of additional options; click the "JRE" tab 
and enter the following VM Arguments:
+
+`-Xmx600m`
+
+`-Dtapestry.execution-mode=development`
+
+image::run-configuration-jre.png[]
+
+Finally, click btn:[Run].
+
+Again, the first time, there's a dizzying number of downloads, but before you 
know it, the Jetty servlet container is up and running.
+
+image::console-startup.png[]
+
+_Note the red square icon above. Later on you'll use that icon to stop Jetty 
before restarting the app._
+
+You can now open a web browser to http://localhost:8080/tutorial1/ to see the 
running application:
+
+image::startpage.png[]
+
+NOTE: Your screen may look very different depending on the version of Tapestry 
you are using!
+
+The date and time in the middle of the page shows that this is a live 
application.
+
+This is a complete little web app. It doesn't do much, but it demonstrate how 
to create a number of pages sharing a common layout, and demonstrates some 
simple navigation and link handling.
+You can see that it has several different pages that share a common layout.
+(_Layout_ is a loose term meaning common look and feel and navigation across 
many or all of the pages of an application.
+Often an application will include a Layout component to provide that 
commonness.)
+
+Next: xref:exploring-the-project.adoc[]
diff --git a/modules/tutorial/pages/dependencies-tools-and-plugins.adoc 
b/modules/tutorial/pages/dependencies-tools-and-plugins.adoc
new file mode 100644
index 0000000..c769503
--- /dev/null
+++ b/modules/tutorial/pages/dependencies-tools-and-plugins.adoc
@@ -0,0 +1,35 @@
+= Dependencies, Tools and Plugins
+As much as we would like to dive right into the code, we must first set up 
your development environment. Likely you have some of these, or reasonable 
alternatives, already on your development machine.
+
+== JDK 1.8 or Newer
+This tutorial uses the latest released version of Tapestry, which requires 
Java Development Kit (JDK) version 1.8 or newer.
+(But see xref:ROOT:supported-environments-and-versions.adoc[] if you want to 
use an older version of JDK or Tapestry.)
+
+== Eclipse IDE
+For this tutorial we'll assume you're using Eclipse as your Integrated 
Development Environment (IDE).
+Eclipse is a popular IDE, but feel free to adapt these instructions to 
IntelliJ, NetBeans, or any other.
+
+Eclipse comes in various flavors, and includes a reasonable XML editor 
built-in.
+It can be downloaded from the eclipse.org web site.
+We recommend the latest version of Eclipse IDE for Java Developers (but 
anything from version 3.7 onward should work fine).
+
+== Apache Maven 3
+Maven is a software build tool with the ability to automatically download 
project dependencies (such as the Tapestry JAR files, and the JAR files that 
Tapestry itself depends on) from one of several central repositories.
+
+Maven is not essential for using Tapestry, but is especially helpful when 
performing the initial set-up of a Tapestry application.
+Feel free to substitute Gradle or Ivy if you prefer.
+
+Eclipse comes with a Maven plugin, M2Eclipse (also known as m2e) with an 
embedded version of Maven.
+We'll use that here for simplicity's sake.
+Alternatively, you could install Maven from 
http://maven.apache.org/download.html and use it from the command line (`mvn`).
+
+== Jetty
+For simplicity, this tutorial uses Jetty, a lightweight open source web server 
and servlet container available from the Eclipse Foundation. Of course, you 
could use pretty much any other Java servlet container (Tomcat, Glassfish, 
JBoss, etc), but the instructions that follow assume Jetty.
+
+We will use Maven to download and run Jetty automatically, so you will NOT 
have to download it for this tutorial. (Alternatively, you could download and 
install the RunJettyRun Eclipse plugin from the Eclipse Marketplace.)
+
+== Tapestry
+Tapestry is available as a set of JAR files, but you will not have to download 
them yourself.
+As with Jetty, Maven will take care of downloading Tapestry and its 
dependencies.
+
+Next: xref:creating-the-skeleton-application.adoc[]
diff --git a/modules/tutorial/pages/exploring-the-project.adoc 
b/modules/tutorial/pages/exploring-the-project.adoc
new file mode 100644
index 0000000..1ce01dc
--- /dev/null
+++ b/modules/tutorial/pages/exploring-the-project.adoc
@@ -0,0 +1,449 @@
+= Exploring the Project
+
+The layout of the project follows the sensible standards promoted by Maven:
+
+* Java source files under `src/main/java`
+* Web application files under `src/main/webapp` (including 
`src/main/webapp/WEB-INF`)
+* Java test sources under `src/test/java`
+* Non-code resources (including Tapestry page and component templates) under 
`src/main/resources` and `src/test/resources`
+
+Let's look at what Maven has created from the archetype, starting with the 
web.xml configuration file:
+
+.src/main/webapp/WEB-INF/web.xml
+[source,xml]
+----
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE web-app
+        PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+        "http://java.sun.com/dtd/web-app_2_3.dtd";>
+<web-app>
+    <display-name>tutorial1 Tapestry 5 Application</display-name>
+    <context-param>
+        <!-- The only significant configuration for Tapestry 5, this informs 
Tapestry
+of where to look for pages, components and mixins. -->
+        <param-name>tapestry.app-package</param-name>
+        <param-value>com.example.tutorial1</param-value>
+    </context-param>
+    <!--
+    Specify some additional Modules for two different execution
+    modes: development and qa.
+    Remember that the default execution mode is production
+    -->
+    <context-param>
+        <param-name>tapestry.development-modules</param-name>
+        <param-value>
+            com.example.tutorial1.services.DevelopmentModule
+        </param-value>
+    </context-param>
+    <context-param>
+        <param-name>tapestry.qa-modules</param-name>
+        <param-value>
+            com.example.tutorial1.services.QaModule
+        </param-value>
+    </context-param>
+    <filter>
+        <filter-name>app</filter-name>
+        <filter-class>org.apache.tapestry5.TapestryFilter</filter-class>
+    </filter>
+    <filter-mapping>
+        <filter-name>app</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+</web-app>
+----
+
+This is short and sweet: you can see that the package name you provided 
earlier shows up as the `tapestry.app-package` context parameter; the 
TapestryFilter instance will use this information to locate the Java classes 
for pages and components.
+
+Tapestry operates as a _servlet filter_ rather than as a traditional servlet.
+In this way, Tapestry has a chance to intercept all incoming requests, to 
determine which ones apply to Tapestry pages (or other resources).
+The net effect is that you don't have to maintain any additional configuration 
for Tapestry to operate, regardless of how many pages or components you add to 
your application.
+
+Much of the rest of web.xml is configuration to match Tapestry execution modes 
against module classes.
+An execution mode defines how the application is being run: the default 
execution mode is "production", but the `web.xml` defines two additional modes: 
"development" and "qa" (for "Quality Assurance").
+The module classes indicated will be loaded for those execution modes, and can 
change the configuration of the application is various ways.
+We'll come back to execution modes and module classes later in the tutorial.
+
+Tapestry pages minimally consist of an ordinary Java class plus a component 
template file.
+
+In the root of your web application, a page named "Index" will be used for any 
request that specifies no additional path after the context name.
+
+== Index Java Class
+Tapestry has very specific rules for where page classes go.
+Tapestry adds a sub-package, `pages`, to the root application package 
(`com.example.tutorial1`); the Java classes for pages goes there.
+Thus the full Java class name is `com.example.tutorial1.pages.Index`.
+
+.src/main/java/com/example/tutorial/pages/Index.java
+[source,java]
+----
+package com.example.tutorial1.pages;
+
+import org.apache.tapestry5.Block;
+import org.apache.tapestry5.EventContext;
+import org.apache.tapestry5.SymbolConstants;
+import org.apache.tapestry5.annotations.InjectPage;
+import org.apache.tapestry5.annotations.Log;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.ioc.annotations.Symbol;
+import org.apache.tapestry5.services.HttpError;
+import org.apache.tapestry5.services.ajax.AjaxResponseRenderer;
+import org.slf4j.Logger;
+
+import java.util.Date;
+
+/**
+ * Start page of application tutorial1.
+ */
+public class Index
+{
+    @Inject
+    private Logger logger;
+
+    @Inject
+    private AjaxResponseRenderer ajaxResponseRenderer;
+
+    @Property
+    @Inject
+    @Symbol(SymbolConstants.TAPESTRY_VERSION)
+    private String tapestryVersion;
+
+    @InjectPage
+    private About about;
+
+    @Inject
+    private Block block;
+
+    // Handle call with an unwanted context
+    Object onActivate(EventContext eventContext)
+    {
+        return eventContext.getCount() > 0 ? new HttpError(404, "Resource not 
found") : null;
+    }
+
+    Object onActionFromLearnMore()
+    {
+        about.setLearn("LearnMore");
+
+        return about;
+    }
+
+    @Log
+    void onComplete()
+    {
+        logger.info("Complete call on Index page");
+    }
+
+    @Log
+    void onAjax()
+    {
+        logger.info("Ajax call on Index page");
+
+        ajaxResponseRenderer.addRender("middlezone", block);
+    }
+
+    public Date getCurrentTime()
+    {
+        return new Date();
+    }
+}
+----
+
+There's a bit going on in this listing, as the Index page attempts to 
demonstrate a bunch of different ideas in Tapestry.
+Even so, the class is essentially pretty simple:
+Tapestry pages and components have no base classes to extend, no interfaces to 
implement, and are just a very pure POJO (Plain Old Java Object) ... with some 
special naming conventions and annotations for fields and methods.
+
+You do have to meet the Tapestry framework partway:
+
+* You need to put the Java class in the expected package, here 
`com.example.tutorial1.pages`.
+* The class must be public.
+* You need to make sure there's a public, no-arguments constructor (here, the 
Java compiler has silently provided one for us).
+* All non-static fields must be *private*.
+
+As we saw when running the application, the page displays the current date and 
time, as well as a couple of extra links.
+The `currentTime` property is where that value comes from; shortly we'll see 
how that value is referenced in the template, so it can be extracted from the 
page and output.
+
+Tapestry always matches a page class to a template; neither is functional 
without the other.
+In fact, components within a page are treated the same way (except that 
components do not always have templates).
+
+You will often hear about the 
http://en.wikipedia.org/wiki/Model_view_controller[Model-View-Controller 
pattern (MVC)].
+In Tapestry, the page class acts as both the Model (the source of data) and 
the controller (the logic that responds to user interaction).
+The template is the View in MVC.
+As a model, the page exposes JavaBeans properties that can be referenced in 
the template.
+
+Let's look at how the component template builds on the Java class to provide 
the full user interface.
+
+== Component Template
+Tapestry pages are the combination of a 
https://en.wikipedia.org/wiki/Plain_old_Java_object[POJO] Java class with a 
Tapestry component template.
+The template has the same name as the Java class, but has the extension `.tml`.
+Since the Java class here is `com.example.tutorial.pages.Index`, the template 
file will be located at 
`src/main/resources/com/example/tutorial/pages/Index.tml`.
+Ultimately, both the Java class and the component template file will be stored 
in the same folder within the deployed WAR file.
+
+Tapestry component templates are well-formed XML documents.
+This means that you can use any available XML editor.
+Templates may even have a DOCTYPE or an XML schema to validate the structure 
of the template page.
+
+[NOTE]
+====
+Tapestry parses component templates using a non-validating parser; it only 
checks for well-formedness: proper syntax, balanced elements, attribute values 
are quoted, and so forth.
+It is reasonable for your build process to perform some kind of template 
validation, but Tapestry accepts the template as-is, as long as it parses 
cleanly.
+====
+
+For the most part, a Tapestry component template looks like ordinary XHTML:
+
+.src/main/resources/com/example/tutorial1/pages/Index.tml
+[source,xml]
+----
+<html t:type="layout" title="tutorial1 Index"
+      xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd";
+      xmlns:p="tapestry:parameter">
+
+    <div class="hero-unit">
+        <p>
+            <img src="${asset:context:images/tapestry.png}"
+                 alt="${message:greeting}" title="${message:greeting}"/>
+        </p>
+        <h3>${message:greeting}</h3>
+        <p>The current time is: <strong>${currentTime}</strong></p>
+        <p>
+            This is a template for a simple marketing or informational 
website. It includes a large callout called
+            the hero unit and three supporting pieces of content. Use it as a 
starting point to create something
+            more unique.
+        </p>
+        <p><t:actionlink t:id="learnmore" class="btn btn-primary 
btn-large">Learn more &raquo;</t:actionlink></p>
+    </div>
+
+    <div class="row">
+        <div class="span4">
+            <h2>Normal link</h2>
+            <p>Clink the bottom link and the page refresh with event 
<code>complete</code></p>
+            <p><t:eventlink event="complete" class="btn 
btn-default">Complete&raquo;</t:eventlink></p>
+        </div>
+        <t:zone t:id="middlezone" class="span4">
+
+        </t:zone>
+        <div class="span4">
+            <h2>Ajax link</h2>
+            <p>Click the bottom link to update just the middle column with 
Ajax call with event <code>ajax</code></p>
+            <p><t:eventlink event="ajax" zone="middlezone" class="btn 
btn-default">Ajax&raquo;</t:eventlink></p>
+        </div>
+    </div>
+
+    <t:block t:id="block">
+        <h2>Ajax updated</h2>
+        <p>I'v been updated through AJAX call</p>
+        <p>The current time is: <strong>${currentTime}</strong></p>
+    </t:block>
+
+</html>
+----
+
+[IMPORTANT]
+====
+You do have to name your component template file, Index.tml, with the exact 
same case as the component class name, Index.
+If you get the case wrong, it may work on some operating systems (such as Mac 
OS X, Windows) and not on others (Linux, and most others).
+This can be really vexing, as it is common to develop on Windows and deploy on 
Linux or Solaris, so be careful about case in this one area.
+====
+
+The goal in Tapestry is for component templates, such as `Index.tml`, to look 
as much as possible like ordinary, static HTML files.
+(By static, we mean unchanging, as opposed to a dynamically generated Tapestry 
page.)
+
+In fact, the expectation is that in many cases, the templates will start as 
static HTML files, created by a web developer, and then be instrumented to act 
as live Tapestry pages.
+
+Tapestry hides non-standard elements and attributes inside XML namespaces. By 
convention, the prefix `t:` is used for the primary namespace, but that is not 
a requirement, any prefix you want to use is fine.
+
+This short template demonstrates quite a few features of Tapestry.
+
+[NOTE]
+====
+Part of the concept of the quickstart archetype is to demonstrate a bunch of 
different features, approaches, and common patterns used in Tapestry.
+So yes, we're hitting you with a lot all at once.
+====
+
+=== Namespaces
+
+First of all, there are two XML namespaces commonly defined:
+
+.src/main/resources/com/example/tutorial1/pages/Index.tml (partial)
+[source,xml]
+----
+<html t:type="layout" title="tutorial1 Index"
+      xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd"; <1>
+      xmlns:p="tapestry:parameter"> <2>
+----
+<1> :t namespace: used to identify Tapestry-specific elements and attributes. 
Although there is an XSD (that is, a XML schema definition), it is incomplete 
(for reasons explained shortly)
+<2> :p namespace: A way of marking a chunk of the template as a parameter 
passed into another component. We'll expand on that shortly.
+
+A Tapestry component template consists mostly of standard XHTML that will pass 
down to the client web browser unchanged.
+The dynamic aspects of the template are represented by _components_ and 
_expansions_.
+
+=== Expansions in Templates
+
+Let's start with expansions.
+Expansions are an easy way of including some dynamic output when rendering the 
page.
+By default, an expansion refers to a JavaBeans property of the page:
+
+[source.xml]
+----
+<p>The current time is: ${currentTime}</p>
+----
+
+The value inside the curly braces is a _property expression_.
+Tapestry uses its own property expression language that is expressive, fast, 
and type-safe.
+
+TIP: Tapestry does NOT use reflection to implement property expressions.
+
+More advanced property expressions can traverse multiple properties (for 
example, `user.address.city`), or even invoke public methods.
+Here the expansion simply reads the `currentTime` property of the page.
+
+Tapestry follows the rules defined by Sun's JavaBeans specification: a 
property name of currentTime maps to two methods: `getCurrentTime()` and 
`setCurrentTime()`.
+If you omit one or the other of these methods, the property is either read 
only (as here), or write only.
+(Keep in mind that as far as JavaBeans properties go, it's the methods that 
count; the names of the instance variables, or even whether they exist, is 
immaterial.)
+
+Tapestry does go one step further: it ignores case when matching properties 
inside the expansion to properties of the page.
+In the template we could say `$\{currenttime}` or `$\{CurrentTime}` or any 
variation, and Tapestry will still invoke the `getCurrentTime()` method.
+
+Note that in Tapestry it is not necessary to configure what object holds the 
`currentTime` property.
+A template and a page are always used in combination with each other; 
expressions are always rooted in the page instance, in this case, an instance 
of the `Index` class.
+
+The `Index.tml` template includes a second expansion:
+
+[source,xml]
+----
+<p>${message:greeting}</p>
+----
+
+Here `greeting` is not a property of the page; its actually a localized 
message key.
+Every Tapestry page and component is allowed to have its own message catalog.
+(There's also a global message catalog, which we'll describe later.)
+
+.src/main/resources/com/example/tutorial/pages/Index.properties
+[source]
+----
+greeting=Welcome to Tapestry 5!  We hope that this project template will get 
you going in style.
+----
+
+Message catalogs are useful for storing repeating strings outside of code or 
templates, though their primary purpose is related to localization of the 
application (which will be described in more detail in a later chapter).
+Messages that may be used across multiple pages can be stored in the 
application's global message catalog, `src/main/webapp/WEB-INF/app.properties`, 
instead.
+
+This `message:` prefix is not some special case; there are actually quite a 
few of these binding prefixes built into Tapestry, each having a specific 
purpose.
+In fact, omitting a binding prefix in an expansion is exactly the same as 
using the `prop:` binding prefix, which means to treat the binding as a 
property expression.
+
+Expansions are useful for extracting a piece of information and rendering it 
out to the client as a string, but the real heavy lifting of Tapestry occurs 
inside components.
+
+== Components Inside Templates
+Components can be represented inside a component template in two ways:
+
+1. As an ordinary element, but with a t:type attribute to define the type of 
component.
+2. As an element in the Tapestry namespace, in which case the element name 
determines the type.
+
+Here we've used an `<html>` element to represent the application's `Layout` 
component.
+
+[source,xml]
+----
+<html t:type="layout" ...> 
+  ...
+</html>
+----
+
+But for the `EventLink` component, we've used an element in the Tapestry 
namespace:
+
+[source,xml]
+----
+<t:eventlink page="Index">refresh page</t:eventlink>
+----
+
+Which form you select is a matter of choice. In the vast majority of cases, 
they are exactly equivalent.
+
+As elsewhere, case is ignored.
+Here the types ("layout" and "eventlink") were in all lower case; the actual 
class names are `Layout` and `EventLink`.
+Further, Tapestry "blends" the core library components in with the components 
defined by this application; thus type "layout" is mapped to application 
component class `com.example.tutorial.components.Layout`, but "eventlink" is 
mapped to Tapestry's built-in 
`org.apache.tapestry5.corelib.components.EventLink` class.
+
+Tapestry components are configured using parameters.
+For each component, there is a set of parameters, each with a specific type 
and purpose.
+Some parameters are required, others are optional.
+Attributes of the element are used to bind parameters to specific literal 
values, or to page properties.
+Tapestry is flexible here as well; you can always place an attribute in the 
Tapestry namespace (using the `t:` prefix), but in most cases, this is 
unnecessary.
+
+[source,xml]
+----
+<html t:type="layout" title="tutorial1 Index"
+      p:sidebarTitle="Framework Version" ...
+----
+
+This binds two parameters, `title` and `sidebarTitle`, of the `Layout` 
component to the literal strings "tutorial1 Index" and "Framework Version", 
respectively.
+
+The Layout component will actually provide the bulk of the HTML ultimately 
sent to the browser; we'll look at its template in a later chapter.
+The point is, the page's template is integrated into the Layout component's 
template.
+The following diagram shows how parameters passed to the Layout component end 
up rendered in the final page:
+
+image:templates_and_parameters.png[]
+
+The interesting point here (and this is an advanced concept in Tapestry, one 
we'll return to later) is that we can pass a chunk of the `Index.tml` template 
to the `Layout` component as the `sidebar` parameter.
+That's what the `tapestry:parameter` namespace (the `p:` prefix) is for; the 
element name is matched against a parameter of the component and the entire 
block of the template is passed into the `Layout` component ... which decides 
where, inside its template, that block gets rendered.
+
+[source,xml]
+----
+<t:eventlink event="complete" class="btn 
btn-default">Complete&raquo;</t:eventlink>
+----
+
+This time, it's the `page` parameter of the `PageLink` component that is 
bound, to the literal value "Index" (which is the name of this page).
+This gets rendered as a URL that re-renders the page, which is how the current 
time gets updated.
+You can also create links to other pages in the application and, as we'll see 
in later chapters, attach additional information to the URL beyond just the 
page name.
+
+== A Magic Trick
+
+Now it's time for a magic trick.
+Edit `Index.java` and change the `getCurrentTime()` method to:
+
+.Index.java (partial)
+[source,java]
+----
+public String getCurrentTime()
+{
+  return "A great day to learn Tapestry";
+}
+----
+
+Make sure you save changes; then click the refresh link in the web browser:
+
+image:app-live-reload.png[]
+
+This is one of Tapestry's early wow factor features: changes to your component 
classes are picked up immediately (a feature we call Live Class Reloading).
+No restart.
+No re-deploy.
+Make the changes and see them now.
+Nothing should slow you down or get in the way of you getting your job done.
+
+[TIP]
+====
+If Live Class Reloading isn't working for you, check 
xref:userguide::class-reloading.adoc#_troubleshooting_live_class_reloading[Troubleshooting
 Live Class Reloading].
+====
+
+But... what if you make a mistake?
+What if you got the name in the template wrong.
+Give it a try; in the template, change $\{currentTime} to, say, 
$\{currenTime}, and see what you get:
+
+image:app-error-1.png[]
+
+This is Tapestry's exception report page.
+It's quite detailed.
+It clearly identifies what Tapestry was doing, and relates the problem to a 
specific line in the template, which is shown in context.
+Tapestry always expands out the entire stack of exceptions, because it is so 
common for exceptions to be thrown, caught, and re-thrown inside other 
exceptions.
+In fact, if we scroll down just a little bit, we see more detail about this 
exception, plus a little bit of help:
+
+image:app-error-2.png[]
+
+This is part of Tapestry's way: it not only spells out exactly what it was 
doing and what went wrong, but it even helps you find a solution; here it tells 
you the names of properties you could have used.
+
+[NOTE]
+====
+This level of detail reflects that the application has been configured to run 
in _development mode_ instead of _production mode_.
+In production mode, the exception report would simply be the top level 
exception message.
+However, most production applications go further and customize how Tapestry 
handles and reports exceptions.
+====
+
+Tapestry displays the stack trace of the deepest exception, along with lots of 
details about the run-time environment: details about the current request, the 
`HttpSession` (if one exists), and even a detailed list of all JVM system 
properties.
+Scroll down to see all this information.
+
+Next: xref:implementing-the-hi-lo-guessing-game.adoc[]
+
+
diff --git a/modules/tutorial/pages/implementing-the-hi-lo-guessing-game.adoc 
b/modules/tutorial/pages/implementing-the-hi-lo-guessing-game.adoc
new file mode 100644
index 0000000..512d4be
--- /dev/null
+++ b/modules/tutorial/pages/implementing-the-hi-lo-guessing-game.adoc
@@ -0,0 +1,510 @@
+= Implementing the Hi-Lo Guessing Game
+
+Let's start building a basic Hi-Lo Guessing game.
+
+In the game, the computer selects a number between 1 and 10. You try and guess 
the number, clicking links. At the end, the computer tells you how many guesses 
you required to identify the target number. Even a simple example like this 
will demonstrate several important concepts in Tapestry:
+
+* Breaking an application into individual pages
+* Transferring information from one page to another
+* Responding to user interactions
+* Storing client information in the server-side session
+
+We'll build this little application in small pieces, using the kind of 
iterative development that Tapestry makes so easy.
+
+.Flow Diagram
+[plantuml,hilo-flow,svg,role=flow]
+----
+include::partial$diagrams/hilo-flow.puml[]
+----
+
+Our page flow is very simple, consisting of three pages: Index (the starting 
page), Guess and GameOver.
+The Index page introduces the application and includes a link to start 
guessing.
+The Guess page presents the user with ten links, plus feedback such as "too 
low" or "too high".
+The GameOver page tells the user how many guesses they took before finding the 
target number.
+
+== Index Page
+
+Let's get to work on the Index page and template. Make Index.tml look like 
this:
+
+.src/main/resources/com/example/tutorial1/pages/Index.tml
+[source,xml]
+----
+<html t:type="layout" title="Hi/Lo Guess"
+    xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd";>
+
+    <p>
+        I'm thinking of a number between one and ten ...
+    </p>
+    <p>
+        <a href="#">start guessing</a>
+    </p>
+
+</html>
+----
+
+And edit the corresponding Java class, Index.java, removing its body (but you 
can leave the imports in place for now):
+
+.src/main/java/com/example/tutorial1/pages/Index.java
+[source,java]
+----
+package com.example.tutorial1.pages;
+
+public class Index
+{
+}
+----
+
+Running the application gives us our start:
+
+image:hilo-1.png[]
+
+However, clicking the link doesn't do anything yet, as its just a placeholder 
<a> tag, not an actual Tapestry component.
+Let's think about what should happen when the user clicks that link:
+
+* A random target number between 1 and 10 should be selected.
+* The number of guesses taken should be reset to 0.
+* The user should be sent to the Guess page to make a guess.
+
+Our first step is to find out when the user clicks that "start guessing" link.
+In a typical web application framework, we might start thinking about URLs and 
handlers and maybe some sort of XML configuration file.
+But this is Tapestry, so we're going to work with components and methods on 
our classes.
+
+First, the component.
+We want to perform an action (selecting the number) before continuing on to 
the Guess page.
+The ActionLink component is just what we need; it creates a link with a URL 
that will trigger an action event in our code ... but that's getting ahead of 
ourselves.
+First up, convert the `<a>` tag to an `ActionLink` component:
+
+.Index.tml (partial)
+[source.xml]
+----
+<p>
+    <t:actionlink t:id="start">start guessing</t:actionlink>
+</p>
+----
+
+If you refresh the browser and hover your mouse over the "start guessing" 
link, you'll see that its URL is now `/tutorial1/index.start`, which identifies 
the name of the page ("index") and the id of the component ("start").
+
+If you click the link now, you'll get an error:
+
+image:hilo-index-missing-action-error.png[]
+
+Tapestry is telling us that we need to provide some kind of event handler for 
that event.
+What does that look like?
+
+An event handler is a method of the Java class with a special name.
+The name is on__Eventname__From__Component-id__ ... here we want a method 
named `onActionFromStart()`.
+How do we know that "action" is the right event name?
+Because that's what `ActionLink` does, that's why its named `ActionLink`.
+
+Once again, Tapestry gives us options; if you don't like naming conventions, 
there's an `@OnEvent` annotation you can place on the method instead, which 
restores the freedom to name the method as you like.
+Details about this approach are in the Tapestry Users' Guide.
+We'll be sticking with the naming convention approach for the tutorial.
+
+When handling a component event request (the kind of request triggered by the 
`ActionLink` component's URL), Tapestry will find the component and trigger a 
component event on it. This is the callback our server-side code needs to 
figure out what the user is doing on the client side.
+Let's start with an empty event handler:
+
+.Index.java
+[source,java]
+----
+package com.example.tutorial1.pages;
+
+public class Index
+{
+    void onActionFromStart()
+    {
+
+    }
+}
+----
+
+In the browser, we can re-try the failed component event request by hitting 
the refresh button ... or we can restart the application.
+In either case, we get the default behavior, which is simply to re-render the 
page.
+
+Note that the event handler method does not have to be public; it can be 
protected, private, or package private (as in this example).
+By convention, such methods are package private, if for no other reason than 
it is the minimal amount of characters to type.
+
+Hmm... right now you have to trust us that the method got invoked.
+That's no good ... what's a quick way to tell for sure?
+One way would be have the method throw an exception, but that's a bit ugly.
+
+How about this: add the 
javadoc:org.apache.tapestry5.annotations.Log[label=@Log] annotation to the 
method:
+
+.Index.java (partial)
+[source,java]
+----
+import org.apache.tapestry5.annotations.Log;
+
+...
+
+    @Log
+    void onActionFromStart()
+    {
+
+    }
+...
+----
+
+When you next click the link you should see the following in the Eclipse 
console:
+
+----
+[DEBUG] pages.Index [ENTER] onActionFromStart()
+[DEBUG] pages.Index [ EXIT] onActionFromStart
+[INFO] AppModule.TimingFilter Request time: 3 ms
+[INFO] AppModule.TimingFilter Request time: 5 ms
+----
+
+The `@Log` annotation directs Tapestry to log method entry and exit.
+You'll get to see any parameters passed into the method, and any return value 
from the method ... as well as any exception thrown from within the method.
+It's a powerful debugging tool.
+This is an example of Tapestry's meta-programming power, something we'll use 
quite a bit of in the tutorial.
+
+Why do we see two requests for one click?
+Tapestry uses an approach based on the Post/Redirect/Get pattern.
+In fact, Tapestry generally performs a redirect after each component event.
+So the first request was to process the action, and the second request was to 
re-render the Index page.
+You can see this in the browser, because the URL is still "/tutorial1" (the 
URL for rendering the Index page).
+We'll return to this in a bit.
+
+We're ready for the next step, which involves tying together the Index and 
Guess pages.
+Index will select a target number for the user to Guess, then "pass the baton" 
to the Guess page.
+
+== Guess Page
+
+Let's start by thinking about the Guess page.
+It needs a variable to store the target value in, and it needs a method that 
the Index page can invoke, to set up that target value.
+
+.Guess.java
+[source,java]
+----
+package com.example.tutorial1.pages;
+
+public class Guess
+{
+    private int target;
+
+    void setup(int target)
+    {
+        this.target = target;
+    }
+}
+----
+
+Create that Guess.java file in the same folder as Index.java. Next, we can 
modify Index to invoke the setup() method of our new Guess page class:
+
+.Index.java (revised)
+[source,java]
+----
+package com.example.tutorial1.pages;
+
+import java.util.Random;
+
+import org.apache.tapestry5.annotations.InjectPage;
+import org.apache.tapestry5.annotations.Log;
+
+public class Index
+{
+    private final Random random = new Random(System.nanoTime());
+
+    @InjectPage
+    private Guess guess;
+
+    @Log
+    Object onActionFromStart()
+    {
+        int target = random.nextInt(10) + 1;
+
+        guess.setup(target);
+        return guess;
+    }
+}
+----
+
+The new event handler method now chooses the target number, and tells the 
Guess page about it.
+Because Tapestry is a managed environment, we don't just create an instance of 
Guess ... it is Tapestry's responsibility to manage the life cycle of the Guess 
page.
+Instead, we ask Tapestry for the Guess page, using the `@InjectPage` 
annotation.
+
+IMPORTANT: All fields in a Tapestry page or component class must be non-public.
+
+Once we have that Guess page instance, we can invoke methods on it normally.
+
+Returning a page instance from an event handler method directs Tapestry to 
send a client-side redirect to the returned page, rather than sending a 
redirect for the active page.
+Thus once the user clicks the "start guessing" link, they'll see the Guess 
page.
+
+IMPORTANT: When creating your own applications, make sure that the objects 
stored in final variables are thread safe. It seems counter-intuitive, but 
final variables are shared across many threads. Ordinary instance variables are 
not. Fortunately, the implementation of Random is, in fact, thread safe.
+
+So ... let's click the link and see what we get:
+
+image:guess-template-missing.png[]
+
+Ah! We didn't create a Guess page template. Tapestry was really expecting us 
to create one, so we better do so.
+
+.src/main/resources/com/example/tutorial/pages/Guess.tml
+[source,java]
+----
+<html t:type="layout" title="Guess The Number"
+    xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd";>
+
+    <p>
+        The secret number is: ${target}.
+    </p>
+  
+</html>
+----
+
+Hit the browser's back button, then click the "start guessing" link again. 
We're getting closer:
+
+image:guess-no-target-prop.png[]
+
+If you scroll down, you'll see the line of the Guess.tml template that has the 
error. We have a field named target, but it is private and there's no 
corresponding property, so Tapestry was unable to access it.
+
+We just need to write the missing JavaBeans accessor methods `getTarget()` 
(and `setTarget()` for good measure). Or we could let Tapestry write those 
methods instead:
+
+[source,java]
+----
+@Property
+private int target;
+----
+
+The `@Property` annotation very simply directs Tapestry to write the getter 
and setter method for you.
+You only need to do this if you are going to reference the field from the 
template.
+
+We are getting very close but there's one last big oddity to handle.
+Once you refresh the page you'll see that target is 0!
+
+image:guess-target-zero.png[]
+
+What gives? We know it was set to at least 1 ... where did the value go?
+
+As noted above, Tapestry sends a redirect to the client after handling the 
event request.
+That means that the rendering of the page happens in an entirely new request.
+Meanwhile, at the end of each request, Tapestry wipes out the value in each 
instance variable.
+So that means that target was a non-zero number during the component event 
request ... but by the time the new page render request comes up from the web 
browser to render the Guess page, the value of the target field has reverted 
back to its default, zero.
+
+The solution here is to mark which fields have values that should persist from 
one request to the next (and next, and next ...). That's what the `@Persist` 
annotation is for:
+
+[source,java]
+----
+@Property  
+@Persist
+private int target;
+----
+
+This doesn't have anything to do with database persistence (that's coming up 
in a later chapter).
+It means that the value is stored in the `HttpSession` between requests.
+
+Go back to the Index page and click the link again. Finally, we have a target 
number:
+
+image:guess-target.png[]
+
+That's enough for us to get started. Let's build out the Guess page, and get 
ready to let the user make guesses.
+We'll show the count of guesses, and increment that count when they make them.
+We'll worry about high and low and actually selecting the correct value later.
+
+When building Tapestry pages, you sometimes start with the Java code and build 
the template to match, and sometime start with the template and build the Java 
code to match.
+Both approaches are valid.
+Here, lets start with the markup in the template, then figure out what we need 
in the Java code to make it work.
+
+.Guess.tml (revised)
+[source,java]
+----
+<html t:type="layout" title="Guess The Number"
+    xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd";
+    xmlns:p="tapestry:parameter">
+ 
+    <p>
+        The secret number is: ${target}.
+    </p>
+ 
+    <strong>Guess number ${guessCount}</strong>
+ 
+    <p>Make a guess from the options below:</p>
+ 
+    <ul class="list-inline">
+        <t:loop source="1..10" value="current">
+            <li>
+            <t:actionlink t:id="makeGuess" context="current">${current}
+            </t:actionlink>
+            </li>
+        </t:loop>
+    </ul>
+ 
+</html>
+----
+
+So it looks like we need a `guessCount` property that starts at 1.
+
+We're also seeing one new component, the Loop component.
+A Loop component iterates over the values passed to it in its `source` 
parameter, and renders it body once for each value.
+It updates the property bound to its `value` parameter before rendering its 
body.
+
+That special property expression, `1..10`, generates a series of numbers from 
1 to 10, inclusive.
+Usually, when you use the Loop component, you are iterating over a List or 
Collection of values, such as the results of a database query.
+
+So, the Loop component is going to set the `current` property to 1, and render 
its body (the `<li>` tag, and the ActionLink component).
+Then its going to set the `current` property to 2 and render its body again 
... all the way up to 10.
+
+And notice what we're doing with the ActionLink component; its no longer 
enough to know the user clicked on the ActionLink ... we need to know _which 
iteration_ the user clicked on.
+The `context` parameter allows a value to be added to the ActionLink's URL, 
and we can get it back in the event handler method.
+
+TIP: The URL for the ActionLink will be `/tutorial1/guess.makeguess/3`. That's 
the page name, "Guess", the component id, "makeGuess", and the context value, 
"3".
+
+.Guess.java (revised)
+[source,java]
+----
+package com.example.tutorial1.pages;
+
+import org.apache.tapestry5.annotations.Persist;
+import org.apache.tapestry5.annotations.Property;
+
+public class Guess
+{
+    @Property
+    @Persist
+    private int target, guessCount;
+
+    @Property
+    private int current;
+
+    void setup(int target)
+    {
+        this.target = target;
+        guessCount = 1;
+    }
+
+    void onActionFromMakeGuess(int value)
+    {
+        guessCount++;
+    }
+
+}
+----
+
+The revised version of Guess includes two new properties: `current` and 
`guessCount`.
+There's also a handler for the action event from the `makeGuess` ActionLink 
component; currently it just increments the count.
+
+Notice that the `onActionFromMakeGuess()` method now has a parameter: the 
context value that was encoded into the URL by the ActionLink.
+When then user clicks the link, Tapestry will automatically extract the string 
from the URL, convert it to an int and pass that int value into the event 
handler method.
+More boilerplate code you don't have to write.
+
+At this point, the page is partially operational:
+
+image:guess-1.png[]
+
+Our next step is to actually check the value provided by the user against the 
target and provide feedback: either they guessed too high, or too low, or just 
right.
+If they get it just right, we'll switch to the `GameOver` page with a message 
such as "You guessed the number 5 in 2 guesses".
+
+Let's start with the Guess page; it now needs a new property to store the 
message to be displayed to the user, and needs a field for the injected 
GameOver page:
+
+.Guess.java (partial)
+[source,java]
+----
+@Property
+@Persist(PersistenceConstants.FLASH)
+private String message;
+
+@InjectPage
+private GameOver gameOver;
+----
+
+First off, we're seeing a variation of the `@Persist` annotation, where a 
persistence _strategy_ is provided by name.
+`FLASH` is a built-in strategy that stores the value in the session, but only 
for one request ... it's designed specifically for these kind of feedback 
messages.
+If you hit F5 in the browser, to refresh, the page will render but the message 
will disappear.
+
+Next, we need some more logic in the `onActionFromMakeGuess()` event handler 
method:
+
+.Guess.java (partial)
+[source,java]
+----
+Object onActionFromMakeGuess(int value)
+{
+    if (value == target)
+    {
+         gameOver.setup(target, guessCount);
+         return gameOver;
+    }
+
+    guessCount++;
+
+    message = String.format("Your guess of %d is too %s.", value,
+         value < target ? "low" : "high");
+
+    return null;
+}
+----
+
+Again, very straight-forward.
+If the value is correct, then we configure the GameOver page and return it, 
causing a redirect to that page.
+Otherwise, we increment the number of guesses, and format the message to 
display to the user.
+
+In the template, we just need to add some markup to display the message:
+
+.Guess.tml (partial)
+[source,xml]
+----
+<strong>Guess number ${guessCount}</strong>
+
+<t:if test="message">
+    <p>
+        <strong>${message}</strong>
+    </p>
+</t:if>
+----
+
+This snippet uses Tapestry's `If` component.
+The `If` component evaluates its test parameter and, if the value evaluates to 
true, renders its body.
+The property bound to test doesn't have to be a boolean; Tapestry treats 
`null` as false, it treats zero as false and non-zero as true, it treats an 
empty `Collection` as `false` ... and for Strings (such as `message`) it treats 
a blank string (one that is null, or consists only of white space) as false, 
and a non-blank string is true.
+
+## GameOver Page
+
+GameOver.java
+
+[source,java]
+----
+package com.example.tutorial1.pages;
+
+import org.apache.tapestry5.annotations.Persist;
+import org.apache.tapestry5.annotations.Property;
+
+public class GameOver
+{
+    @Property
+    @Persist
+    private int target, guessCount;
+       
+    void setup(int target, int guessCount)
+    {
+        this.target = target;
+        this.guessCount = guessCount;
+    }
+}
+----
+
+.GameOver.tml
+[source,xml]
+----
+<html t:type="layout" title="Game Over"
+    xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd";
+    xmlns:p="tapestry:parameter">
+
+    <p>
+        You guessed the number
+        <strong>${target}</strong>
+        in
+        <strong>${guessCount}</strong>
+        guesses.
+    </p>
+  
+</html>
+----
+
+The result, when you guess correctly, should be this:
+
+image:gameover.png[]
+
+That wraps up the basics of Tapestry; we've demonstrated the basics of linking 
pages together and passing information from page to page in code as well as 
incorporating data inside URLs.
+
+There's still more room to refactor this toy application; for example, making 
it possible to start a new game from the GameOver page (and doing it in a way 
that doesn't duplicate code). In addition, later we'll see other ways of 
sharing information between pages that are less cumbersome than the 
setup-and-persist approach shown here.
+
+Next up: let's find out how Tapestry handles HTML forms and user input.
+
+Next: xref:using-beaneditform-to-create-user-forms.adoc[]
diff --git a/modules/tutorial/pages/index.adoc 
b/modules/tutorial/pages/index.adoc
new file mode 100644
index 0000000..67065e3
--- /dev/null
+++ b/modules/tutorial/pages/index.adoc
@@ -0,0 +1,57 @@
+= Introduction
+
+Welcome to Tapestry!
+
+This is a tutorial for people who will be creating Tapestry web applications.
+It doesn't matter whether you have experience with earlier versions of 
Tapestry or other web frameworks.
+In fact, in some ways, the less you know about web development in general, the 
better off you may be ... that much less to unlearn!
+
+You do need to have a reasonable understanding of HTML, a smattering of XML, 
and a good understanding of basic Java language features, including Annotations.
+
+== The Challenges of Web Application Development
+If you're used to developing web applications using servlets and JSPs, or with 
Struts, you are simply used to a lot of pain.
+These are environments with few safety net; Struts and the Servlet API have no 
idea how your application is structured, or how the different pieces fit 
together.
+Any URL can be an action and any action can forward to any view (usually a 
JSP) to provide an HTML response to the web browser.
+The pain is the unending series of small, yet important, decisions you have to 
make as a developer (and communicate to the rest of your team).
+What are the naming conventions for actions, for pages, for attributes stored 
in the `HttpSession` or `HttpServletRequest`?
+Where do cross-cutting concerns such as database transactions, caching and 
security get implemented (and do you have to cut-and-paste Java or XML to make 
it work?)
+How are your packages organized ... where to the user interface classes go, 
and where do the data and entity objects go?
+How do you share code from one part of your application to another?
+
+On top of all that, the traditional approaches thrust something most unwanted 
in your face: _multi-threaded coding_.
+Remember back to Object Oriented Programming 101 where an object was defined 
as a bundle of data and operations on that data?
+You have to unlearn that lesson as soon as you build a traditional web 
application, because web applications are multi-threaded.
+An application server could be handling dozens or hundreds of requests from 
individual users, each in their own thread, and each sharing the exact same 
objects.
+Suddenly, you can't store data inside an object (a servlet or a Struts Action) 
because whatever data you store for one user will be instantly overwritten by 
some other user.
+
+Worse, your objects each have only one operation: `doGet()` or `doPost()`.
+
+Meanwhile, most of your day-to-day work involves deciding how to package up 
some data already inside a particular Java object and squeeze that data into a 
URL's query parameters, so that you can write more code to convert it back if 
the user clicks that particular link.
+And don't forget editing a bunch of XML files to keep the servlet container, 
or the Struts framework, aware of these decisions.
+
+Just for laughs, remember that you have to rebuild, redeploy and restart your 
application after virtually any change.
+Is any of this familiar?
+Then perhaps you'd appreciate something a little less familiar: Tapestry.
+
+== The Tapestry Way
+Tapestry uses a very different model: a structured, organized world of pages, 
and components within pages.
+Everything has a very specific name (that you provide). Once you know the name 
of a page, you know the location of the Java class for that page, the location 
of the template for that page, and the total structure of the page.
+Tapestry knows all this as well, and can make things *just work*.
+
+As we'll see in the following pages, Tapestry lets you code in terms of your 
objects.
+You'll barely see any Tapestry classes, outside of a few Java annotations.
+If you have information to store, store it as fields of your classes, not 
inside the `HttpServletRequest` or `HttpSession`.
+If you need some code to execute, it's just a simple annotation or method 
naming convention to get Tapestry to invoke that method, at the right time, 
with the right data.
+
+Tapestry also shields you from most of the multi-threaded aspects of web 
application development.
+Tapestry manages the life cycle of your page and components objects, and the 
fields of the pages and components, in a thread-safe way.
+Your page and component classes always look like simple, standard 
http://en.wikipedia.org/wiki/Plain_Old_Java_Object[POJOs].
+
+Tapestry began in January 2000, and it now reflects over twenty years of 
experience of the entire Tapestry community.
+Tapestry brings to the table all that experience about the best ways to build 
scalable, maintainable, robust, internationalized, and Ajax-enabled 
applications.
+
+== Getting the Tutorial Source
+Although you won't need it, the source code for this tutorial is available on 
https://github.com/hlship/tapestry5-tutorial[GitHub].
+
+== Time to Begin
+Okay, enough background. Now let's dig in: 
xref:dependencies-tools-and-plugins.adoc[]
diff --git 
a/modules/tutorial/pages/using-beaneditform-to-create-user-forms.adoc 
b/modules/tutorial/pages/using-beaneditform-to-create-user-forms.adoc
new file mode 100644
index 0000000..87a8ee4
--- /dev/null
+++ b/modules/tutorial/pages/using-beaneditform-to-create-user-forms.adoc
@@ -0,0 +1,415 @@
+= Using BeanEditForm To Create User Forms
+
+In the previous chapters, we saw how Tapestry can handle simple links, even 
links that pass information in the URL.
+In this chapter, we'll see how Tapestry can do the same, and quite a bit more, 
for HTML forms.
+
+Form support in Tapestry is deep and rich, more than can be covered in a 
single chapter.
+However, we can show the basics, including some very common development 
patterns.
+To get started, let's create a simple address book application.
+
+We'll start with the entity data, a simple object to store the information 
we'll need.
+These classes go in an `entities` sub-package.
+Unlike the use of the `pages` sub-package (for page component classes), this 
is not enforced by Tapestry; it's just a convention (but as we'll see shortly, 
a handy one).
+
+Tapestry treats public fields as if they were JavaBeans properties; since the 
Address object is just "dumb data", there's no need to get carried away writing 
getters and setters. Instead, we'll define an entity that is all public fields:
+
+.src/main/java/com/example/tutorial/entities/Address.java
+
+[source,java]
+----
+package com.example.tutorial1.entities;
+
+import com.example.tutorial1.data.Honorific;
+
+public class Address
+{
+    public Honorific honorific;
+    public String firstName;
+    public String lastName;
+    public String street1;
+    public String street2;
+    public String city;
+    public String state;
+    public String zip;
+    public String email;
+    public String phone;
+}
+----
+
+We also need to define the enum type, Honorific:
+
+.src/main/java/com/example/tutorial/data/Honorific.java
+[source,java]
+----
+package com.example.tutorial1.data;
+
+public enum Honorific
+{
+    MR, MRS, MISS, DR
+}
+----
+
+== Address Pages
+
+We're probably going to create a few pages related to addresses: pages for 
creating them, for editing them, for searching and listing them. We'll create a 
sub-folder, address, to hold them. Let's get started on the first of these 
pages, "address/Create" (that's the real name, including the slash — we'll see 
in a minute how that maps to classes and templates).
+
+First, we'll update the Index.tml template, to create a link to the new page:
+
+.src/main/resources/com/example/tutorial/pages/Index.tml (partial)
+
+[source,xml]
+----
+<h1>Address Book</h1>
+
+<ul>
+    <li><t:pagelink page="address/create">Create new address</t:pagelink></li>
+</ul>
+----
+
+Now we need the address/Create page; lets start with an empty shell, just to 
test our navigation.
+
+.src/main/resources/com/example/tutorial/pages/address/CreateAddress.tml
+[source,xml,subs="+attributes"]
+----
+<html t:type="layout" title="Create New Address"
+    xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd";> <1>
+
+    <em>coming soon ...</em>
+
+</html>
+----
+<1> Adapt as necessary for earlier versions than Tapestry 5.4, for 
`tapestry_5_3.xsd`
+
+Next, the corresponding class:
+
+.src/main/java/com/example/tutorial/pages/address/CreateAddress.java
+[source,java]
+----
+package com.example.tutorial1.pages.address;
+
+public class CreateAddress
+{
+
+}
+----
+
+So ... why is the class named `CreateAddress` and not simply `Create`?
+Actually, we could have named it `Create"`, and the application would still 
work, but the longer _class_ name is equally valid.
+Tapestry noticed the redundancy in the class name 
(`com.example.tutorial1.pages.__address__.Create__Address__`) and just stripped 
out the redundant suffix.
+
+Tapestry actually creates a bunch of aliases for you pages; any of these 
aliases are valid and can appear in URLs or in the page parameter of PageLink.
+You can see the list in the console:
+
+----
+[INFO] TapestryModule.ComponentClassResolver Available pages (12):
+              (blank): com.example.tutorial1.pages.Index
+   ComponentLibraries: org.apache.tapestry5.corelib.pages.ComponentLibraries
+             Error404: com.example.tutorial1.pages.Error404
+      ExceptionReport: org.apache.tapestry5.corelib.pages.ExceptionReport
+             GameOver: com.example.tutorial1.pages.GameOver
+                Guess: com.example.tutorial1.pages.Guess
+                Index: com.example.tutorial1.pages.Index
+          PageCatalog: org.apache.tapestry5.corelib.pages.PageCatalog
+PropertyDisplayBlocks: org.apache.tapestry5.corelib.pages.PropertyDisplayBlocks
+   PropertyEditBlocks: org.apache.tapestry5.corelib.pages.PropertyEditBlocks
+        ServiceStatus: org.apache.tapestry5.corelib.pages.ServiceStatus
+          T5Dashboard: org.apache.tapestry5.corelib.pages.T5Dashboard
+       address/Create: com.example.tutorial1.pages.address.CreateAddress
+address/CreateAddress: com.example.tutorial1.pages.address.CreateAddress
+----
+
+Tapestry users the shortest alias when constructing URLs.
+
+Eventually, your application will probably have more entities: perhaps you'll 
have a `user/Create` page and a `payment/Create` page and an `account/Create` 
page.
+You _could_ have a bunch of different classes all named Create spread across a 
number of different packages.
+That's legal Java, but it isn't ideal.
+You may find yourself accidentally editing the Java code for creating an 
Account when you really want to be editing the code for creating a Payment.
+
+Tapestry is encouraging you to use a more descriptive name: 
`Create__Address__`, not just `Create`, but it isn't making you pay the cost 
(in terms of longer, uglier URLs).
+The URL to access the page will still be 
`http://localhost:8080/tutorial1/address/create`.
+
+And remember, regardless of the name that Tapestry assigns to your page, the 
template file is named like the Java class itself: `CreateAddress.tml`.
+
+[NOTE]
+====
+Index pages work in folders as well.
+A class named `com.example.tutorial1.pages.address.AddressIndex` would be 
given the name `address/Index`.
+However, Tapestry has special rules for pages named `Index` and the rendered 
URL would be `http://localhost:8080/tutorial1/address/`.
+In other words, you can place Index pages in any folder and Tapestry will 
build a short URL for that page ... and you don't have to keep naming the 
classes `Index` (it's confusing to have many classes with the same name, even 
across multiple packages); instead, you can name each index page after the 
package that contains it.
+Tapestry users a smart convention to keep it all straight and generate short, 
to the point URLs.
+====
+
+== Using the BeanEditForm Component
+
+Time to start putting together the logic for this form.
+Tapestry has a specific component for client-side Forms: the 
javadoc:org.apache.tapestry5.corelib.components.Form[] component, as well as 
components for form controls, such as 
javadoc:org.apache.tapestry5.corelib.components.Checkbox[] and 
javadoc:org.apache.tapestry5.corelib.components.TextField[].
+We'll cover those in a bit more detail later .. instead, we're again going to 
let Tapestry do the heavy lifting for us, via the 
javadoc:org.apache.tapestry5.corelib.components.BeanEditForm[] component.
+
+Add the following to the `CreateAddress` template (replacing the "coming soon 
..." message):
+
+.CreateAddress.tml (partial)
+[source,xml]
+----
+<t:beaneditform object="address"/>
+----
+
+And match that up with a property in the `CreateAddress` class:
+
+.CreateAddress.java (partial)
+[source,java]
+----
+@Property
+private Address address;
+----
+
+When you refresh the page, you may see a warning like the following at the top 
of the page:
+
+image:hmac-warning.png[]
+
+If you see that, it means you need to invent an HMAC passphrase for your app.
+Just edit your `AppModule.java` class (in your `services` package), adding a 
couple of lines to the `contributeApplicationDefaults` method like the 
following:
+
+.AppModule.java (partial)
+----
+// Set the HMAC pass phrase to secure object data serialized to client
+configuration.add(SymbolConstants.HMAC_PASSPHRASE, "");
+----
+
+but, instead of an empty string, insert a long, *random string of characters* 
(like a very long and complex password, at least 30 characters) that you keep 
private.
+
+After you do that, stop the app and restart it, and click on the Create new 
address link again, and you'll see something like this:
+
+image:create-address-initial.png[]
+
+Tapestry has done quite a bit of work here.
+It has created a form that includes a field for each property.
+Further, it has seen that the honorific property is an enumerated type, and 
presented that as a drop-down list.
+
+In addition, Tapestry has converted the property names (`city`, `email`, 
`firstName`) to user presentable labels ("City", "Email", "First Name").
+In fact, these are `<label>` elements, so clicking a label with the mouse will 
move the input cursor into the corresponding field.
+
+This is an awesome start; it's a presentable interface, quite nice in fact for 
a few minute's work.
+But it's far from perfect; let's get started with some customizations.
+
+=== Changing Field Order
+
+The `BeanEditForm` must guess at the right order to present the fields; for 
public fields, they end up in alphabetical order.
+For standard JavaBeans properties, the `BeanEditForm` default is in the order 
in which the getter methods are defined in the class (it uses line number 
information, if available).
+
+A better order for these fields is the order in which they are defined in the 
`Address` class:
+
+* honorific
+* firstName
+* lastName
+* street1
+* street2
+* city
+* state
+* zip
+* email
+* phone
+
+We can accomplish this by using the `reorder` parameter of the `BeanEditForm` 
component, which is a comma separated list of property (or public field) names:
+
+.CreateAddress.tml (partial)
+[source,xml]
+----
+<t:beaneditform object="address"
+        
reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone"
 />
+----
+
+image:create-address-reordered.png[]
+
+=== Customizing labels
+Tapestry makes it pretty easy to customize the labels used on the fields.
+It's just a matter of creating a _message catalog_ for the page.
+
+In Tapestry, every page and component may have its own message catalog.
+This is a standard Java properties file, and it is named the same as the page 
or component class, with a `.properties` extension.
+A message catalog consists of a series of lines, each line is a message key 
and a message value separated with an equals sign.
+
+All it takes is to create a message entry with a particular name: the name of 
the property suffixed with `-label`.
+As elsewhere, Tapestry is forgiving of case.
+
+.src/main/resources/com/example/tutorial/pages/address/CreateAddress.properties
+[source,properties]
+----
+street1-label=Street 1
+street2-label=Street 2
+email-label=E-Mail
+zip-label=Zip Code
+phone-label=Phone Number
+----
+
+Since this is a _new_ file (and not a change to an existing file), you may 
have to restart Jetty to force Tapestry to pick up the change.
+
+image:address-v3.png[]
+
+We can also customize the options in the drop down list.
+All we have to do is add some more entries to the message catalog matching the 
enum names to the desired labels.
+Update `CreateAddress.properties` and add:
+
+[source,properties]
+----
+MR=Mr.
+MRS=Mrs.
+DR=Dr.
+----
+
+Notice that we don't have to include an option for MISS, because that is 
converted to "Miss" anyway.
+You might just want to include it for sake of consistency ... the point is, 
each option label is searched for separately.
+
+Lastly, the default label on the submit button is "Create/Update" 
(BeanEditForm doesn't know how it is being used).
+Let's change that to "Create Address".
+
+That button is a component within the `BeanEditForm` component.
+It's not a property, so we can't just put a message into the message catalog, 
the way we can with the fields.
+Fortunately, the BeanEditForm component includes a parameter expressly for 
re-labeling the button.
+Simply change the `CreateAddress` component template:
+
+.CreateAddress.tml (partial)
+[source,xml]
+----
+<t:beaneditform submitlabel="Create Address" object="address"
+        
reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone"/>
+----
+
+The default for the submitlabel parameter is "Create/Update", but here we're 
overriding that default to a specific value.
+
+The final result shows the reformatting and relabelling:
+
+image:address-v5.png[]
+
+Before continuing on to validation, a side note about message catalogs.
+Message catalogs are not just for re-labeling fields and options; we'll see in 
later chapters how message catalogs are used in the context of localization and 
internationalization.
+
+Instead of putting the label for the submit button directly inside the 
template, we're going to provide a reference to the label; the actual label 
will go in the message catalog.
+
+In Tapestry, when binding a parameter, the value you provide may include a 
prefix.
+The prefix guides Tapestry in how to interpret the rest of the the parameter 
value ... is it the name of a property? The id of a component?
+A message key? Most parameters have a default prefix, usually `prop:`, that is 
used when you fail to provide one (this helps to make the templates as terse as 
possible).
+
+Here we want to reference a message from the catalog, so we use the `message:` 
prefix:
+
+[source,xml]
+----
+<t:beaneditform object="address" submitlabel="message:submit-label"
+    
reorder="honorific,firstName,lastName,street1,street2,city,state,zip,email,phone"
 />
+----
+
+And then we define the submit-label key in the message catalog:
+
+[source,properties]
+----
+submit-label=Create Address
+----
+
+In the end, the exact same HTML is sent to the client, regardless of whether 
you include the label text directly in the template, or indirectly in the 
message catalog.
+In the long term, the latter approach will work better if you later chose to 
internationalize your application.
+
+=== Adding Validation
+Before we worry about storing the Address object, we should make sure that the 
user provides reasonable values.
+For example, several of the fields should be required, and phone numbers and 
email address have specific formats.
+
+The `BeanEditForm` checks for a Tapestry-specific annotation, 
javadoc:org.apache.tapestry5.beaneditor.Validate[label=@Validate], on the 
field, the getter method, or the setter method of each property.
+
+Edit the `Address` entity, and update the `lastName`, `firstName`, `street1`, 
`city`, `state` and `zip` fields, adding a `@Validate` annotation to each:
+
+[source,java]
+----
+@Validate("required")
+public String firstName;
+----
+
+What is that string, `required`?
+That's how you specify the desired validation.
+It is a series of names that identify what type of validation is desired.
+A number of validators are built in, such as `required`, `minLength` and 
`maxLength`. As elsewhere, Tapestry is case insensitive.
+
+You can apply multiple validations, by separating the validator names with 
commas.
+Some validators can be configured (with an equals sign).
+Thus you might say `required,minLength=5` for a field that must be specified, 
and must be at least five characters long.
+
+[IMPORTANT]
+====
+You can easily get confused when you make a change to an entity class, such as 
adding the `@Validate` annotation, and _not_ see the result in the browser.
+Only component classes, and (most) classes in the Tapestry services layer, are 
live-reloaded.
+Data and entity objects are not reloaded, so this is one area where you need 
to stop and restart Jetty to see the change.
+====
+
+Restart the application, and refresh your browser, then hit the Create Address 
button.
+
+image:address-v6.png[]
+
+This is a shot just after hitting the Create Address button; all the fields 
have been validated and errors displayed.
+Each field in error has been highlighted in red and had an error message added.
+Further, the label for each of the fields has also been highlighted in red, to 
even more clearly identify what's in error.
+The cursor has also been moved to the first field that's in error.
+And _all_ of this is taking place on the client side, without any 
communication with the application.
+
+Once all the errors are corrected, and the form does submit, all validations 
are performed on the server side as well (just in case the client has 
JavaScript disabled).
+
+So ... how about some more interesting validation than just "required or not".
+Tapestry has built in support for validating based on field length and several 
variations of field value, including regular expressions.
+Zip codes are pretty easy to express as a regular expression.
+
+[source,java]
+----
+@Validate("required,regexp=^\\d{5}(-\\d{4})?$")
+public String zip;
+----
+
+Let's give it a try; restart the application and enter an "abc" for the zip 
code.
+
+image:address-v7.png[]
+
+This is what you'll see after typing "abc" and clicking the Create Address 
button.
+
+TIP: Modern browsers will automatically validate a regexp field when the form 
is submitted, as shown above. Older browsers do not have that automatic 
support, but will still validate input, using the same decorations as for the 
required fields in the previous screenshot.
+
+In any case, that's the right validation behavior, but it's the wrong message. 
Your users are not going to know or care about regular expressions.
+
+Fortunately, it's easy to customize validation messages.
+All we need to know is the name of the property ("zip") and the name of the 
validator ("regexp").
+We can then put an entry into the CreateAddress message catalog:
+
+[source,properties]
+----
+zip-regexp-message=Zip Codes are five or nine digits.  Example: 02134 or 
90125-1655.
+----
+
+Refresh the page and submit again:
+
+image:address-v8.png[]
+
+This trick isn't limited to just the regexp validator, it works equally well 
with any validator.
+
+Let's go one step further.
+Turns out, we can move the regexp pattern to the message catalog as well.
+If you only provide the name of the validator in the `@Validate` annotation, 
Tapestry will search the containing page's message catalog of the constraint 
value, as well as the validation message.
+The constraint value for the regexp validator is the regular expression to 
match against.
+
+[source,java]
+----
+@Validate("required,regexp")
+public String zip;
+----
+
+Now, just put the regular expression into the CreateAddress message catalog:
+
+[source,properties]
+----
+zip-regexp=^\\d{5}(-\\d{4})?$
+zip-regexp-message=Zip Codes are five or nine digits.  Example: 02134 or 
90125-1655.
+----
+
+After a restart you'll see the ... the same behavior.
+But when we start creating more complicated regular expressions, it'll be 
much, much nicer to put them in the message catalog rather than inside the 
annotation value.
+And inside the message catalog, you can change and tweak the regular 
expressions without having to restart the application each time.
+
+We could go a bit further here, adding more regular expression validation for 
phone numbers and e-mail addresses.
+We're also far from done in terms of further customizations of the 
`BeanEditForm` component.
+
+By now you are likely curious about what happens after the form submits 
successfully (without validation errors), so that's what we'll focus on next.
+
+Next: xref:using-tapestry-with-hibernate.adoc[]
diff --git a/modules/tutorial/pages/using-tapestry-with-hibernate.adoc 
b/modules/tutorial/pages/using-tapestry-with-hibernate.adoc
new file mode 100644
index 0000000..f114217
--- /dev/null
+++ b/modules/tutorial/pages/using-tapestry-with-hibernate.adoc
@@ -0,0 +1,262 @@
+= Using Tapestry With Hibernate
+
+So, you fill in all the fields, submit the form (without validation errors) 
and voila: you get back the same form, blanked out.
+What happened, and where did the data go?
+
+What happened is that we haven't told Tapestry what to do after the form is 
successfully submitted (by successful, we mean, with no validation errors).
+Tapestry's default behavior is to redisplay the active page, and that occurs 
in a new request, with a new instance of the Address object (because the 
address field is not a peristent field).
+
+Well, since we're creating objects, we might as well store them somewhere ... 
in a database.
+We're going to quickly integrate Tapestry with 
http://hibernate.org/[Hibernate] as the object/relational mapping layer, and 
ultimately store our data inside a http://www.hsqldb.org/[HyperSQL] (HSQLDB) 
database.
+HSQLDB is an embedded database engine and requires no installation – it will 
be pulled down as a dependency by Maven.
+
+== Re-configuring the Project
+We're going to bootstrap this project from a simple Tapestry project to one 
that uses Hibernate and HSQLDB.
+
+=== Updating the Dependencies
+First, we must update the POM to list a new set of dependencies, that includes 
Hibernate, the Tapestry/Hibernate integration library, and the HSQLDB JDBC 
driver:
+
+.pom.xml (partial)
+[source,xml]
+----
+<dependencies>
+
+    <dependency>
+        <groupId>org.apache.tapestry</groupId>
+        <artifactId>tapestry-hibernate</artifactId>
+        <version>${tapestry-release-version}</version>
+    </dependency>
+
+    <dependency>
+        <groupId>org.hsqldb</groupId>
+        <artifactId>hsqldb</artifactId>
+        <version>2.3.2</version>
+    </dependency>
+    ...
+</dependencies>
+----
+
+The `tapestry-hibernate` library includes, as transitive dependencies, 
Hibernate and `tapestry-core`.
+This means that you can simply replace `tapestry-core` with 
`tapestry-hibernate` inside the `<artifactId>` element.
+
+After changing the POM and saving, Maven should automatically download the 
JARs for the new dependencies.
+
+=== Hibernate Configuration
+Hibernate needs a master configuration file, `hibernate.cfg.xml`, used to 
store connection and other data.
+Create this in your `src/main/resources` folder:
+
+.src/main/resources/hibernate.cfg.xml
+[source,xml]
+----
+<!DOCTYPE hibernate-configuration PUBLIC
+        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
+        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd";>
+<hibernate-configuration>
+    <session-factory>
+        <property 
name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
+        <property 
name="hibernate.connection.url">jdbc:hsqldb:./target/work/t5_tutorial1;shutdown=true</property>
+        <property 
name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
+        <property name="hibernate.connection.username">sa</property>
+        <property name="hibernate.connection.password"></property>
+        <property name="hbm2ddl.auto">update</property>
+        <property name="hibernate.show_sql">true</property>
+        <property name="hibernate.format_sql">true</property>
+    </session-factory>
+</hibernate-configuration>
+----
+
+Most of the configuration is to identify the JDBC driver and connection URL.
+
+Note the connection URL.
+We are instructing HSQLDB to store its database files within our project's 
target directory.
+We are also instructing HSQLDB to flush any data to these files at shutdown.
+This means that data will persist across different invocations of this 
project, but if the target directory is destroyed (e.g., via `mvn clean`), then 
all the database contents will be lost.
+
+In addition, we are configuring Hibernate to _update_ the database schema; 
when Hibernate initializes it will create or even modify tables to match the 
entities.
+Finally, we are configuring Hibernate to output any SQL it executes, which is 
very useful when initially building an application.
+
+But what entities?
+Normally, the available entities are listed inside `hibernate.cfg.xml`, but 
that's not necessary with Tapestry; in another example of convention over 
configuration, Tapestry locates all entity classes inside the entities package 
(`com.example.tutorial1.entities` in our case) and adds them to the 
configuration.
+Currently, that is just the Address entity.
+
+== Adding Hibernate Annotations
+For an entity class to be used with Hibernate, some Hibernate annotations must 
be added to the class.
+
+Below is the updated Address class, with the Hibernate annotations (as well as 
the Tapestry ones).
+
+.src/main/java/com/example/tutorial/entities/Address.java
+[source,java]
+----
+package com.example.tutorial1.entities;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+
+import org.apache.tapestry5.beaneditor.NonVisual;
+import org.apache.tapestry5.beaneditor.Validate;
+
+import com.example.tutorial1.data.Honorific;
+
+@Entity
+public class Address
+{
+  @Id
+  @GeneratedValue(strategy = GenerationType.IDENTITY)
+  @NonVisual
+  public Long id;
+
+  public Honorific honorific;
+
+  @Validate("required")
+  public String firstName;
+
+  @Validate("required")
+  public String lastName;
+
+  public String street1;
+
+  public String street2;
+
+  @Validate("required")
+  public String city;
+
+  @Validate("required")
+  public String state;
+
+  @Validate("required,regexp")
+  public String zip;
+
+  public String email;
+
+  public String phone;
+}
+----
+
+The Tapestry annotations, `@NonVisual` and `@Validate`, may be placed on the 
setter or getter method or on the field (as we have done here).
+As with the Hibernate annotations, putting the annotation on the field 
requires that the field name match the corresponding property name.
+
+* `@NonVisual` – indicates a field, such as a primary key, that should not be 
made visible to the user.
+* `@Validate` – identifies the validations associated with a field.
+
+At this point you should stop and restart your application.
+
+== Updating the Database
+So we have a database set up, and Hibernate is configured to connect to it.
+Let's make use of that to store our Address object in the database.
+
+What we need is to provide some code to be executed when the form is submitted.
+When a Tapestry form is submitted, there is a whole series of events that get 
fired.
+The event we are interested in is the "success" event, which comes late in the 
process, after all the values have been pulled out of the request and applied 
to the page properties, and after all server-side validations have occurred.
+
+The success event is only fired if there are no validation errors.
+
+Our event handler must do two things:
+
+* Use the Hibernate Session object to persist the new Address object.
+* Commit the transaction to force the data to be written to the database.
+
+Let's update our `CreateAddress.java` class:
+
+.src/main/java/com/example/tutorial/pages/address/CreateAddress.java
+[source,java]
+----
+package com.example.tutorial1.pages.address;
+
+import com.example.tutorial1.entities.Address;
+import com.example.tutorial1.pages.Index;
+import org.apache.tapestry5.annotations.InjectPage;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.hibernate.annotations.CommitAfter;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.hibernate.Session;
+
+public class CreateAddress
+{
+    @Property
+    private Address address;
+
+    @Inject
+    private Session session;
+
+    @InjectPage
+    private Index index;
+
+    @CommitAfter
+    Object onSuccess()
+    {
+        session.persist(address);
+
+        return index;
+    }
+}
+----
+
+The `@Inject` annotation tells Tapestry to inject a service into the annotated 
field; Tapestry includes a sophisticated Inversion of Control container 
(similar in many ways to Spring) that is very good at locating available 
services by type, rather than by a string id.
+In any case, the Hibernate Session object is exposed as a Tapestry IoC 
service, ready to be injected (this is one of the things provided by the 
tapestry-hibernate module).
+
+Tapestry automatically starts a transaction as necessary; however that 
transaction will be _aborted_ at the end of the request by default.
+If we make changes to persistent objects, such as adding a new Address object, 
then it is necessary to commit the transaction.
+
+The `@CommitAfter` annotation can be applied to any component method; if the 
method completes normally, the transaction will be committed (and a new 
transaction started to replace the committed transaction).
+
+After persisting the new address, we return to the main Index page of the 
application.
+
+_Note: In real applications, it is rare to have pages and components directly 
use the Hibernate Session. It is generally a better approach to define your own 
Data Access Object layer to perform common update operations and queries._
+
+== Showing Addresses
+As a little preview of what's next, let's display all the Addresses entered by 
the user on the Index page of the application.
+After you enter a few names, it will look something like:
+
+image:index-grid-v1.png[]
+
+== Adding the Grid to the Index page
+So, how is this implemented? Primarily, its accomplished by the `Grid` 
component.
+
+The `Grid` component is based on the same concepts as the `BeanEditForm` 
component; it can pull apart a bean into columns.
+The columns are sortable, and when there are more entries than will fit on a 
single page, page navigation is automatically added.
+
+A minimal `Grid` is very easy to add to the template.
+Just add this near the bottom of `Index.tml`:
+
+.src/main/webapp/Index.tml (partial)
+[source,xml]
+----
+<t:grid source="addresses"
+    include="honorific,firstName,lastName,street1,city,state,zip,phone"/>
+----
+
+Note that the `Grid` component accepts many of the same parameters that we 
used with the `BeanEditForm`.
+Here we use the include parameter to specify the properties to show, and in 
what order.
+
+Now all we have to do is supply the addresses property in the Java code. 
Here's how `Index.java` should look now:
+
+.src/main/java/com/example/tutorial/pages/Index.java
+[source,java]
+----
+package com.example.tutorial1.pages;
+import java.util.List;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.hibernate.Session;
+import com.example.tutorial1.entities.Address;
+public class Index
+{
+    @Inject
+    private Session session;
+    public List<Address> getAddresses()
+    {
+        return session.createCriteria(Address.class).list();
+    }
+}
+----
+
+Here, we're using the Hibernate Session object to find all Address objects in 
the database. Any sorting that takes place will be done in memory.
+This is fine for now (with only a handful of Address objects in the database). 
Later we'll see how to optimize this for very large result sets.
+
+== What's Next?
+We have lots more to talk about: more components, more customizations, 
built-in Ajax support, more common design and implementation patterns, and even 
writing your own components (which is easy!).
+
+Check out the many Tapestry resources available on the Documentation page, 
including the xref:ROOT:getting-started.adoc[] and FAQ pages and the Cookbook.
+Be sure to peruse the xref:userguide::index.adoc[User Guide], which provides 
comprehensive details on nearly every Tapestry topic.
+Finally, be sure to visit (and bookmark) 
https://tapestry-jumpstart.org/jumpstart[Tapestry JumpStart], which provides a 
nearly exhaustive set of tutorials.
diff --git a/modules/tutorial/partials/diagrams/hilo-flow.puml 
b/modules/tutorial/partials/diagrams/hilo-flow.puml
new file mode 100644
index 0000000..cedaa9d
--- /dev/null
+++ b/modules/tutorial/partials/diagrams/hilo-flow.puml
@@ -0,0 +1,10 @@
+@startuml
+hide empty description
+
+[*] -right-> Index
+Index -right-> Guess
+Guess -> Guess
+Guess --> GameOver
+GameOver -right-> [*]
+
+@enduml

Reply via email to